Provide views, controls, and layout structures for declaring your app's user interface using SwiftUI.

Posts under SwiftUI tag

200 Posts

Post

Replies

Boosts

Views

Activity

iOS 26 Remove Liquid Glass UITabBar Selection Indicator
I have subclassed UITabBar and created a custom look and feel for it. On iOS 18, the tab bar appears as expected. On iOS 26, however, a default Liquid Glass-style capsule selection indicator appears behind the selected tab item. I tried using UITabBarAppearance, including selectionIndicatorTintColor = .clear and selectionIndicatorImage = nil / empty image, but the capsule-style selected background still appears. Is this selection treatment part of the new default system rendering in iOS 26, and if so, is there any supported way to remove or disable it while still using UITabBar?
0
0
4
58m
iOS 26 TabBar Remove Selected Capsule
I have subclassed UITabBar and created a custom look and feel for it. On iOS 18, the tab bar appears as expected. On iOS 26, however, a default Liquid Glass-style capsule selection indicator appears behind the selected tab item. I tried using UITabBarAppearance, including selectionIndicatorTintColor = .clear and selectionIndicatorImage = nil / empty image, but the capsule-style selected background still appears. Is this selection treatment part of the new default system rendering in iOS 26, and if so, is there any supported way to remove or disable it while still using UITabBar?
0
0
5
59m
Initial presentation of popover hangs when shown from a button in the toolbar
I have a simple reproducer here: struct ContentView: View { @State private var isOn = false @State private var isPresented = false var body: some View { NavigationStack { Color.blue .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Press here") { isPresented = true } .popover(isPresented: $isPresented) { Color.green .frame(idealWidth: 400, idealHeight: 500) .presentationCompactAdaptation(.popover) } } } } } } When I tap on the button in the toolbar you can see there is a hang then the popover shows. Then every time after there is no longer a hang so this seems like a bug. Any ideas? I'm using Xcode 26.3 and a iPad Pro 13-inch (M5) (26.4) simulator.
0
3
70
1d
Unable to use FoundationModels in older app?
Hi, I'm trying to add FoundationModels to an older project but always get the following error: "Unable to resolve 'dependency' 'FoundationModels' import FoundationModels" The error comes and goes while its compiling and then doesn't run the app. I have my target set to 26.0 (and can't go any higher) and am using Xcode 26 (17E192). Is anyone else having this issue? Thanks, Dan Uff
1
0
114
4d
How to recreate Apple Music mini player transition in SwiftUI
Hello, I am building an audio player app in SwiftUI and trying to recreate the behavior of Apple Music's mini player and full player. I'm struggling to get the animation to seamlessly transition between the mini player and the full player. Currently, it feels disconnected and doesn't resemble the smooth animation seen in Apple Music. What I want to achieve: Full player that expands/collapses from/to the mini player Smooth artwork transition between both states Drag down to collapse the full player Support both newer APIs like tabViewBottomAccessory and older iOS versions Questions: What is the best way to build this transition in SwiftUI? Should I use matchedGeometryEffect or something else? Should this be a custom container instead of fullScreenCover? How would you support both new and older iOS versions? What is the best way to implement drag to dismiss? Thanks for any help! Example code: struct ContentView: View { @State private var isFullPlayerPresented = false var body: some View { TabView { Tab("Home", systemImage: "house") { Text("Home") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.green) } Tab("Library", systemImage: "rectangle.stack.fill") { Text("Library") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.brown) } } .tabViewBottomAccessory(isEnabled: !isFullPlayerPresented) { MiniPlayerView(isFullPlayerPresented: $isFullPlayerPresented) } .fullScreenCover(isPresented: $isFullPlayerPresented) { // Maybe it's not a full screen cover presentation in Apple Music? FullPlayerView(isFullPlayerPresented: $isFullPlayerPresented) } } } Mini player: struct MiniPlayerView: View { @Binding var isFullPlayerPresented: Bool var body: some View { Button { isFullPlayerPresented = true } label: { HStack { Image(systemName: "photo") .resizable() .scaledToFit() .frame(width: 30, height: 30) .clipShape(.rect(cornerRadius: 8)) Spacer() Text("Tap to open full player") Spacer() Button("", systemImage: "play.fill", action: {}) } .padding(.horizontal) .padding(.vertical, 4) } .foregroundStyle(.white) } } Full player: struct FullPlayerView: View { @Binding var isFullPlayerPresented: Bool var body: some View { // This art work needs to snaps to the artwork in mini player Image(systemName: "photo") .resizable() .scaledToFit() .frame(width: 250, height: 250) .clipShape(.rect(cornerRadius: 20)) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.red) .overlay(alignment: .topTrailing) { Button(role: .close) { isFullPlayerPresented = false } .foregroundStyle(.white) .padding() } } }
2
0
217
21h
NavigationSplitView no longer pops back to the root view when selection = nil in iOS 26.4 (with a nested TabView)
In iOS 26.4 (iPhone, not iPad), when a NavigationSplitView is combined with a nested TabView, it no longer pops back to the root sidebar view when the List selection is set to nil. This has been working fine for at least a few years, but has just stopped working in iOS 26.4. Here's a minimal working example: import SwiftUI struct ContentView: View { @State var articles: [Article] = [Article(articleTitle: "Dog"), Article(articleTitle: "Cat"), Article(articleTitle: "Mouse")] @State private var selectedArticle: Article? = nil var body: some View { NavigationSplitView { TabView { Tab { List(articles, selection: $selectedArticle) { article in Button { selectedArticle = article } label: { Text(article.title) } } } label: { Label("Explore", systemImage: "binoculars") } } } detail: { Group { if let selectedArticle { Text(selectedArticle.title) } else { Text("No selected article") } } .navigationBarBackButtonHidden(true) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Close", systemImage: "xmark") { selectedArticle = nil } } } } } } struct Article: Identifiable, Hashable { let id: String let title: String init(articleTitle: String) { self.id = articleTitle self.title = articleTitle } } First, I'm aware that nesting a TabView inside a NavigationSplitView is frowned upon: Apple seems to prefer NavigationSplitView nested inside a Tab. However, for my app, that leads to a very confusing user experience. Users quickly get lost because they end up with different articles open in different tabs and it doesn't align well with my core distinction between two "modes": article selection mode and article reading mode. When the user is in article selection mode (sidebar view), they can pick between different ways of selecting an article (Explore, Bookmarks, History, Search), which are implemented as "tabs". When they pick an article from any tab they jump into article reading mode (the detail view). Second, I'm using .navigationBarBackButtonHidden(true) to remove the auto back button that pops back to the sidebar view. This button does still work in iOS 26.4, even with the nested TabView. However, I can't use the auto back button because my detail view is actually a WebView with its own back/forward logic and UI. Therefore, I need a separate close button to exit from the detail view. My close button sets selectedArticle to nil, which (pre-iOS 26.4) would trigger the NavigationSplitView to pop back to the sidebar view. For some reason, in iOS 26.4 the NavigationSplitView doesn't seem to bind correctly to the List's selection parameter, specifically when there's a TabView nested between them. Or, rather, it binds, but fails to pop back when selection becomes nil. One option is to replace NavigationSplitView with NavigationStack (on iPhone). NavigationStack still works with a nested TabView, but it creates other downstream issues for me (as well as forcing me to branch for iPhone and iPad), so I'd prefer to continue using NavigationSplitView. Does anyone have any ideas about how to work around this problem? Is there some way of explicitly telling NavigationSplitView to pop back to the sidebar view on iPhone? (I've tried setting the column visibility but nothing seems to work). Thanks for any help!
0
0
33
6d
Localization in Swift macOS console Apps.
Is it possible to build localization into console apps, developed in SwiftUI in Xcode26. I have created a catalog, (.xcstrings file) with an English and fr-CA string. I have tried to display the French text without success. I am using the console app to test a package which also has English/French text. English text works fine in both package and the console main, but I cannot generate the French. From what I can discover so far it's not possible without bundling it as a .app, (console app). Looking for anyone who has crossed this bridge.
7
0
277
3d
UI Glitch in Toolbar Menu Picker After Migrating to Xcode 26
I am experiencing a UI issue after migrating my app from Xcode 16 to Xcode 26. In my implementation, I have a toolbar that contains multiple buttons along with a dropdown menu. The hierarchy for dropdown is as follows: **Toolbar → ToolbarItem → View → Menu → Picker ** Prior to Xcode 26, this setup worked smoothly in production builds. The dropdown (Menu + Picker) behaves as expected, and selecting a value triggers loading a dataset containing thousands of records on the screen. However, after upgrading to Xcode 26, I am observing an animation glitch when dismissing the dropdown after a selection is made. Specifically, the dropdown briefly shows a “capsule-like” animation artifact during dismissal, which persists for a few seconds. This visual issue is noticeable and negatively impacts the perceived performance and user experience of the app. This issue is occurring in an already released app built with Xcode 26. Questions: Is this a known issue or regression in Xcode 26 / SwiftUI Menu or Picker components? If yes, are there any known fixes or upcoming Xcode versions where this is resolved? If not, what would be the recommended approach to eliminate or minimize this animation glitch when dismissing the dropdown? Additional Context: The issue appears only after migration to Xcode 26. The dataset loaded after selection is large (thousands of records). The glitch specifically occurs during the dismissal animation of the Menu/Picker. Any guidance or workarounds would be greatly appreciated.
1
0
39
4d
Previews for SwiftUI views in Packages don't work in Xcode 26.4
I have an iOS project based on SwiftUI in which almost all code is organised in Packages. With Xcode 26.2 and 26.3, I can preview all SwiftUI views without issues. With Xcode 26.4, the same previews don't work, in the canvas appears this error message: "Cannot preview in this file. Could not find target description for “TaskListView.swift”". The explanation is: "The list of source files that produce object files did not contain this file to be previewed. Check to make sure it is not excluded using the EXCLUDED_SOURCE_FILE_NAMES build setting." If I add a SwiftUI view to the main project files (not in a package), the preview works as expected. Is it an Xcode 26.4 regression? Or do I need to modify some configuration file?
5
0
169
3d
onDisappear not called when closing a document on macOS (Designed for iPad), works on iPad
When running a SwiftUI DocumentGroup app on macOS designed for iPad, onDisappear is not called when closing a document, and deinit of state objects owned by a ContentView is not invoked. This behavior works as expected on iPad. @main struct MyApp: App { var body: some Scene { DocumentGroup(newDocument: MyDocument()) { file in ContentView(document: file.$document) .onDisappear { print("This isn't called on macOS Designed For iPad, but is on iPad when closing a document.") } } } } It is my understanding that for a macOS designed for iPad these lifecycle events would behave the same - otherwise there appears to be no way to detect if a document has closed on macOS.
1
0
75
1w
How to load and draw texture with opacity in Metal
The background I'm finally working to convert my very old Mac kaleidoscope application, ScopeWorks, which was written in OpenGL and Objective-C, to a Multiplatform app in SwiftUI and Metal. I'm using the MetalKit MTKView class, wrapped for SwiftUI as an NSViewRepresentable or UIViewRepresentable. I then provide an MTKViewDelegate that provides a draw method. The draw method fetches the current render pass descriptor, creates a command buffer, sets up a render pipeline, and does its drawing. My renderer's makePipeline method looks like this: func makePipeline() { let library = device.makeDefaultLibrary() let pipelineDesc = MTLRenderPipelineDescriptor() pipelineDesc.vertexFunction = library?.makeFunction(name: "vertex_main") pipelineDesc.fragmentFunction = library?.makeFunction(name: "fragment_main") pipelineDesc.colorAttachments[0].pixelFormat = .bgra8Unorm pipeline = try! device.makeRenderPipelineState(descriptor: pipelineDesc) } And my shaders look like this: struct VertexOut { float4 position [[position]]; float2 texCoord; }; vertex VertexOut vertex_main(const device float2* position [[buffer(0)]], uint vid [[vertex_id]]) { VertexOut out; float2 pos = position[vid]; out.position = float4(pos, 0, 1); out.texCoord = pos * 0.5 + 0.5; // basic mapping return out; } fragment float4 fragment_main(VertexOut in [[stage_in]], texture2d<float> tex [[texture(0)]], constant float4& color [[buffer(1)]]) { constexpr sampler s(address::repeat, filter::linear); // float4 texColor = tex.sample(s, in.texCoord); // return texColor * color; float4 textureColor = {1, 2, 3, 4}; if (all(color == textureColor)) { return tex.sample(s, in.texCoord); } else { return color; } // Sample the texture directly — no color tint applied return tex.sample(s, in.texCoord); } The first part of my MTKViewDelegate's draw method looks like this: func draw(in view: MTKView) { guard let drawable = view.currentDrawable, let descriptor = view.currentRenderPassDescriptor, let pipeline = pipeline, let texture = texture else { return } let commandBuffer = commandQueue.makeCommandBuffer()! let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor)! encoder.setRenderPipelineState(pipeline) encoder.setFragmentTexture(texture, index: 0) descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0, blue: 0, alpha: 1.0) // Draw six equilateral triangles forming the hexagon let radius: Float = 0.6 for i in 0..<6 { let angle = Float(i) * (.pi / 3) let cosA = cos(angle) let sinA = sin(angle) let nextA = Float(i+1) * (.pi / 3) let cosB = cos(nextA) let sinB = sin(nextA) let verts: [simd_float2] = [ simd_float2(0, 0), simd_float2(radius * cosA, radius * sinA), simd_float2(radius * cosB, radius * sinB) ] encoder.setVertexBytes(verts, length: MemoryLayout<simd_float2>.stride * 3, index: 0) // Tell the fragment shader to use the texture color. var textureColor: simd_float4 = simd_float4(1, 2, 3, 4) encoder.setFragmentBytes(&textureColor, length: MemoryLayout<SIMD4<Float>>.stride, index: 1) encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3) One of the things the existing app does is load PNG or TIFF images with an alpha channel, and then overlay parts of the image on top of themselves flipped, so you get interesting Moiré patterns in the lines in the resulting kaleidoscope. For now I'm working on a single sample image, loading it into a texture in Metal, and just rendering it as a hexagon and drawing lines for the triangles that make up the hexagon. (For now I'm using the vertex coordinates as the texture coordinates, so I get a hexagonal part of my texture rather than a single triangular part tessellated into a hexagon. I'll fix that later.) In both iOS and OS I set the clear color to black at the beginning of the draw function. The issue: The source image is mostly transparent, but with a lot of partly transparent pixels. Here's what it looks like in Photoshop, where you can see the transparent parts as a checkerboard pattern: (I tried to crop the original image to show the approximate part that I'm rendering in a hexagon, but it's not exact. Look for the same shapes in the different images to compare them.) When I render my hexagon in the Metal view in the iOS version of the app, it looks like it's forcing each pixel to fully opaque or fully transparent: And in the macOS version of the app, it seems to force ALL the pixels to opaque: I haven't shown all the setup code, because it's' a lot. Is there some rendering mode setup I'm missing in order to get it to draw the pixels into the output based on their opacity, including partial opacity?
2
0
764
6d
Unwanted animation of navbar controls
What could cause the issue shown on the gif. At first I though clean build folder helps. But when you close the main window and open it after some time it gets back to this state. The whole set of elements in the navbar starts shifting to the right and it continues infinitely 15.6.1 (24G90) Swift 6.1.2
2
0
74
1w
Section(isExpanded:) in sidebar List, inconsistent row animation on collapse/expand
When using Section(_:isExpanded:) inside a List with .listStyle(.sidebar) in a NavigationSplitView, some rows don't animate with the others during collapse and expand. Specific rows (often in the middle of the section) snap in/out instantly while the rest animate smoothly. I've reproduced this with both static views and ForEach. Minimal reproduction: struct SidebarView: View { @State private var sectionExpanded = true @State private var selection: Int? var body: some View { NavigationSplitView { List(selection: $selection) { Section("Section", isExpanded: $sectionExpanded) { ForEach(1...3, id: \.self) { index in NavigationLink(value: index) { Label("Item \(index)", systemImage: "\(index).circle") } } } } .listStyle(.sidebar) .navigationTitle("Sidebar") } detail: { if let selection { Text("Selected item \(selection)") } else { Text("Select an item") } } } } Environment: macOS 26.3, Xcode 26.3, SwiftUI Steps to reproduce: Run the above code in a macOS app Click the section disclosure chevron to collapse Observe that some rows animate out while others snap instantly Expand again — same inconsistency Expected: All rows animate together uniformly. Actual: Some rows (typically middle items) skip the animation entirely. I also tried using static Label views instead of ForEach, same result. Is there a known workaround?
2
0
191
1w
iOS 26: Toolbar button background flashes black during NavigationStack transitions (dark mode)
I’m seeing a visual glitch with toolbar buttons when building with Xcode 26 for iOS 26. During transitions (both pushing in a NavigationStack and presenting a .sheet with its own NavigationStack), the toolbar button briefly flashes the wrong background colour (black in dark mode, white in light mode) before animating to the correct Liquid Glass appearance. This happens even in a minimal example and only seems to affect system toolbar buttons. A custom view with .glassEffect() doesn’t have the issue. I’ve tried: .tint(...), UINavigationBarAppearance/UIToolbarAppearance, and setting backgrounds on hosting/nav/window but none of those made any difference. Here’s a minimal reproducible example: import SwiftUI struct ContentView: View { @State private var showingSheet = false var body: some View { NavigationStack { List { NavigationLink("Push (same stack — morphs)") { DetailView() } Button("Sheet (separate stack — flashes)") { showingSheet = true } } .navigationTitle("Root") .scrollContentBackground(.hidden) .background(.gray) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Action") {} } } .sheet(isPresented: $showingSheet) { SheetView() } } } } struct DetailView: View { var body: some View { Text("Detail (same stack)") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.gray) .navigationTitle("Detail") .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Action") {} } } } } struct SheetView: View { var body: some View { NavigationStack { Text("Sheet (separate stack)") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.gray) .navigationTitle("Sheet") .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Action") {} } } } } } Has anyone else seen this or found a workaround outside of disabling this background completely with .sharedBackgroundVisibility(.hidden)? I have filed a bug report under FB22141183
1
0
316
1w
Glass Effect Label Shadow Clipping During Morph Animation
Hi all, I’m experiencing a visual bug when applying the glass effect to a Label in Liquid Glass (current version 26.2 on simulator; also reproducible in 26.3.1 on device). Issue: On a label with .glassEffect(.regular), when collapsing via morph animation, the shadow is clipped during the animation, and then suddenly "pops" back to its un-clipped state, resulting in a jarring visual effect. Minimal Example: import SwiftUI struct ContentView: View { var body: some View { Menu { Button("Duplicate", action: {}) Button("Rename", action: {}) Button("Delete…", action: {}) } label: { Label("PDF", systemImage: "doc.fill") .padding() .glassEffect(.regular) } } } #Preview { ContentView() } I am not sure if I am misusing the .glassEffect() on the label and maybe there is another more native way of achieving this look? Any advice or workaround suggestions would be greatly appreciated!
2
0
93
1w
PhaseAnimator doesn't reflect @Observable state changes after animation settles
I ran into a behavior with PhaseAnimator that I'm not sure is a bug or by design. I'd appreciate any insight. The Problem When an @Observable property is read only inside a PhaseAnimator content closure, changes to that property are ignored after the animation cycle completes and reaches its resting state. The UI gets stuck showing stale data. Minimal Reproduction I've put together a simple demo with two views side by side, both driven by the same ViewModel and toggled by the same button: BrokenView — receives an @Observable object and reads its property inside PhaseAnimator. After the animation completes, toggling the property has no visible effect. FixedView — receives the same value as a Bool parameter. Updates correctly every time because view's parameter has changed. import SwiftUI @Observable class ViewModel { var isError = false } struct BrokenView: View { let viewModel: ViewModel @State private var trigger = false var body: some View { VStack(spacing: 20) { Text("Broken (@Observable)").font(.headline) PhaseAnimator([false, true], trigger: trigger) { _ in if viewModel.isError { Text("Error!").foregroundStyle(.red).font(.largeTitle) } else { Text("OK").foregroundStyle(.green).font(.largeTitle) } } } .padding() .onAppear { trigger = true } } } struct FixedView: View { let isError: Bool @State private var trigger = false var body: some View { VStack(spacing: 20) { Text("Fixed (Value Type)").font(.headline) PhaseAnimator([false, true], trigger: trigger) { _ in if isError { Text("Error!").foregroundStyle(.red).font(.largeTitle) } else { Text("OK").foregroundStyle(.green).font(.largeTitle) } } } .padding() .onAppear { trigger = true } } } struct DemoView: View { @State private var viewModel = ViewModel() var body: some View { VStack(spacing: 40) { BrokenView(viewModel: viewModel) Divider() FixedView(isError: viewModel.isError) Divider() Button("Toggle isError: \(viewModel.isError)") { viewModel.isError.toggle() } .buttonStyle(.borderedProminent) } .padding() } } Run the preview, then tap the toggle button. FixedView updates instantly; BrokenView stays stuck. My Understanding It seems like PhaseAnimator only tracks @Observable access during active animation phases. Once it settles at rest, the content closure is not re-evaluated, so observation tracking is effectively lost. Passing a value type works because SwiftUI view diffing detects the input change and triggers a body re-evaluation, which in turn re-evaluates the PhaseAnimator content. Question Is this intended behavior? Or shouldn't I use phase animator in this way? I could not find any mention of this limitation in the documentation. If it is by design, it might be worth documenting — it is a subtle pitfall that is easy to miss. Thanks in advance for any input!
0
0
101
2w
How do you support Preferred Font Size / Dynamic Type on macOS?
On macOS 26, how do you support the Preferred Text Size value as defined in the Accessibility Settings? Historically, "Dynamic Type" has not been available on macOS. However, the user has some control over text size through the Accessibility Settings. On macOS 26, a small subset of applications are honouring changes to that value include Finder, Mail, and sidebars in many applications. Dynamic sizing in table views has been available on macOS for awhile. But Mail.app, in particular, is also adjusting the font sizes used in the message's body pane while the Finder is adjusting font sizes used for Desktop icons. I can't find an NSNotification that is fired when the user adjusts the Accessibility Text Size slider, nor can I find an API to read the current value. NSFont.preferredFont(forTextStyle:options:) looks promising but the fonts returned do not appear to take the user's Accessibility setting into account. (Nor do they update dynamically.) SwiftUI's Text("Apple").font(.body) performs similarly to NSFont in that it does respect the style, but it does not honour dynamic sizing. NSFontDescriptor has a bunch of interesting methods, but none that seem to apply to Accessibility Text Size. Given an AppKit label: let label = NSTextField(labelWithString: "AppKit") label.font = NSFont.preferredFont(forTextStyle: .body) Or a SwiftUI label: Text("SwiftUI").font(.body) How do I make either of them responsive to the user's Text Size setting under Accessibility? Note this is on macOS 26 / Xcode 26. I realize there have been some previous forum posts related to this issue but hoping that things might have improved since then.
2
0
445
2w
Back gesture not disabled with navigationBarBackButtonHidden(true) when using .zoom transition
[Submitted as FB22226720] For a NavigationStack destination, applying .navigationBarBackButtonHidden(true) hides the back button and also disables the interactive left-edge back gesture when using the standard push navigation transition. However, when the destination uses .navigationTransition(.zoom), the back button is hidden but the left-edge back gesture is still available—it can still be dismissed even though back is intentionally suppressed. This creates inconsistent behavior between navigation transition styles. navigationBarBackButtonHidden(_:) works with a standard push transition, but not with .navigationTransition(.zoom). In the code below, .interactiveDismissDisabled(true) is also applied as another attempt to suppress the back-swipe gesture, but it has no effect. As a result, there’s currently no clean way to prevent back navigation when using the zoom transition. REPRO STEPS Create an iOS project then replace ContentView with code below, build and run. Leave nav type set to List Push. Open an item. Verify there is no back button, then try the left-edge back gesture. Return to the root view. Change nav type to Grid Zoom. Open an item. Verify there is no back button, then try the left-edge back gesture. ACTUAL In List Push mode, the left-edge back gesture is prevented. In Grid Zoom mode, the back button is hidden, but the left-edge back gesture still works and returns to the previous view. EXPECTED Behavior should be consistent across navigation transition styles. If this configuration is meant to suppress interactive backward navigation for a destination, it should also suppress the left-edge back gesture when using .navigationTransition(.zoom). SCREEN RECORDING SAMPLE CODE struct ContentView: View { private enum NavigationMode: String, CaseIterable { case listPush = "List Push" case gridZoom = "Grid Zoom" } @Namespace private var namespace @State private var navigationMode: NavigationMode = .listPush private let colors: [Color] = [.red, .blue] var body: some View { NavigationStack { VStack(spacing: 16) { Picker("Navigation Type", selection: $navigationMode) { ForEach(NavigationMode.allCases, id: \.self) { mode in Text(mode.rawValue).tag(mode) } } .pickerStyle(.segmented) if navigationMode == .gridZoom { HStack { ForEach(colors.indices, id: \.self) { index in NavigationLink(value: index) { VStack { RoundedRectangle(cornerRadius: 14) .fill(colors[index]) .frame(height: 120) Text("Grid Item \(index + 1)") .font(.subheadline.weight(.medium)) } .padding(12) .frame(maxWidth: .infinity) .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 16)) .matchedTransitionSource(id: index, in: namespace) } .buttonStyle(.plain) } } } else { ForEach(colors.indices, id: \.self) { index in NavigationLink(value: index) { HStack { Circle() .fill(colors[index]) .frame(width: 24, height: 24) Text("List Item \(index + 1)") Spacer() Image(systemName: "chevron.right") .foregroundStyle(.secondary) } .padding() .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 12)) } .buttonStyle(.plain) } } Spacer() } .padding(20) .navigationTitle("Prevent Back Swipe") .navigationSubtitle("Compare Grid Zoom vs List Push") .navigationDestination(for: Int.self) { index in if navigationMode == .gridZoom { DetailView(color: colors[index]) .navigationTransition(.zoom(sourceID: index, in: namespace)) } else { DetailView(color: colors[index]) } } } } } private struct DetailView: View { @Environment(\.dismiss) private var dismiss let color: Color var body: some View { ZStack { color.ignoresSafeArea() Text("Try left-edge swipe back") .font(.title.bold()) .multilineTextAlignment(.center) .padding(.horizontal, 24) } .navigationBarBackButtonHidden(true) .interactiveDismissDisabled(true) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Close", action: dismiss.callAsFunction) } } } }
2
0
605
1w
iOS 26 Remove Liquid Glass UITabBar Selection Indicator
I have subclassed UITabBar and created a custom look and feel for it. On iOS 18, the tab bar appears as expected. On iOS 26, however, a default Liquid Glass-style capsule selection indicator appears behind the selected tab item. I tried using UITabBarAppearance, including selectionIndicatorTintColor = .clear and selectionIndicatorImage = nil / empty image, but the capsule-style selected background still appears. Is this selection treatment part of the new default system rendering in iOS 26, and if so, is there any supported way to remove or disable it while still using UITabBar?
Replies
0
Boosts
0
Views
4
Activity
58m
iOS 26 TabBar Remove Selected Capsule
I have subclassed UITabBar and created a custom look and feel for it. On iOS 18, the tab bar appears as expected. On iOS 26, however, a default Liquid Glass-style capsule selection indicator appears behind the selected tab item. I tried using UITabBarAppearance, including selectionIndicatorTintColor = .clear and selectionIndicatorImage = nil / empty image, but the capsule-style selected background still appears. Is this selection treatment part of the new default system rendering in iOS 26, and if so, is there any supported way to remove or disable it while still using UITabBar?
Replies
0
Boosts
0
Views
5
Activity
59m
Initial presentation of popover hangs when shown from a button in the toolbar
I have a simple reproducer here: struct ContentView: View { @State private var isOn = false @State private var isPresented = false var body: some View { NavigationStack { Color.blue .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Press here") { isPresented = true } .popover(isPresented: $isPresented) { Color.green .frame(idealWidth: 400, idealHeight: 500) .presentationCompactAdaptation(.popover) } } } } } } When I tap on the button in the toolbar you can see there is a hang then the popover shows. Then every time after there is no longer a hang so this seems like a bug. Any ideas? I'm using Xcode 26.3 and a iPad Pro 13-inch (M5) (26.4) simulator.
Replies
0
Boosts
3
Views
70
Activity
1d
Unable to use FoundationModels in older app?
Hi, I'm trying to add FoundationModels to an older project but always get the following error: "Unable to resolve 'dependency' 'FoundationModels' import FoundationModels" The error comes and goes while its compiling and then doesn't run the app. I have my target set to 26.0 (and can't go any higher) and am using Xcode 26 (17E192). Is anyone else having this issue? Thanks, Dan Uff
Replies
1
Boosts
0
Views
114
Activity
4d
How to recreate Apple Music mini player transition in SwiftUI
Hello, I am building an audio player app in SwiftUI and trying to recreate the behavior of Apple Music's mini player and full player. I'm struggling to get the animation to seamlessly transition between the mini player and the full player. Currently, it feels disconnected and doesn't resemble the smooth animation seen in Apple Music. What I want to achieve: Full player that expands/collapses from/to the mini player Smooth artwork transition between both states Drag down to collapse the full player Support both newer APIs like tabViewBottomAccessory and older iOS versions Questions: What is the best way to build this transition in SwiftUI? Should I use matchedGeometryEffect or something else? Should this be a custom container instead of fullScreenCover? How would you support both new and older iOS versions? What is the best way to implement drag to dismiss? Thanks for any help! Example code: struct ContentView: View { @State private var isFullPlayerPresented = false var body: some View { TabView { Tab("Home", systemImage: "house") { Text("Home") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.green) } Tab("Library", systemImage: "rectangle.stack.fill") { Text("Library") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.brown) } } .tabViewBottomAccessory(isEnabled: !isFullPlayerPresented) { MiniPlayerView(isFullPlayerPresented: $isFullPlayerPresented) } .fullScreenCover(isPresented: $isFullPlayerPresented) { // Maybe it's not a full screen cover presentation in Apple Music? FullPlayerView(isFullPlayerPresented: $isFullPlayerPresented) } } } Mini player: struct MiniPlayerView: View { @Binding var isFullPlayerPresented: Bool var body: some View { Button { isFullPlayerPresented = true } label: { HStack { Image(systemName: "photo") .resizable() .scaledToFit() .frame(width: 30, height: 30) .clipShape(.rect(cornerRadius: 8)) Spacer() Text("Tap to open full player") Spacer() Button("", systemImage: "play.fill", action: {}) } .padding(.horizontal) .padding(.vertical, 4) } .foregroundStyle(.white) } } Full player: struct FullPlayerView: View { @Binding var isFullPlayerPresented: Bool var body: some View { // This art work needs to snaps to the artwork in mini player Image(systemName: "photo") .resizable() .scaledToFit() .frame(width: 250, height: 250) .clipShape(.rect(cornerRadius: 20)) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.red) .overlay(alignment: .topTrailing) { Button(role: .close) { isFullPlayerPresented = false } .foregroundStyle(.white) .padding() } } }
Replies
2
Boosts
0
Views
217
Activity
21h
NavigationSplitView no longer pops back to the root view when selection = nil in iOS 26.4 (with a nested TabView)
In iOS 26.4 (iPhone, not iPad), when a NavigationSplitView is combined with a nested TabView, it no longer pops back to the root sidebar view when the List selection is set to nil. This has been working fine for at least a few years, but has just stopped working in iOS 26.4. Here's a minimal working example: import SwiftUI struct ContentView: View { @State var articles: [Article] = [Article(articleTitle: "Dog"), Article(articleTitle: "Cat"), Article(articleTitle: "Mouse")] @State private var selectedArticle: Article? = nil var body: some View { NavigationSplitView { TabView { Tab { List(articles, selection: $selectedArticle) { article in Button { selectedArticle = article } label: { Text(article.title) } } } label: { Label("Explore", systemImage: "binoculars") } } } detail: { Group { if let selectedArticle { Text(selectedArticle.title) } else { Text("No selected article") } } .navigationBarBackButtonHidden(true) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Close", systemImage: "xmark") { selectedArticle = nil } } } } } } struct Article: Identifiable, Hashable { let id: String let title: String init(articleTitle: String) { self.id = articleTitle self.title = articleTitle } } First, I'm aware that nesting a TabView inside a NavigationSplitView is frowned upon: Apple seems to prefer NavigationSplitView nested inside a Tab. However, for my app, that leads to a very confusing user experience. Users quickly get lost because they end up with different articles open in different tabs and it doesn't align well with my core distinction between two "modes": article selection mode and article reading mode. When the user is in article selection mode (sidebar view), they can pick between different ways of selecting an article (Explore, Bookmarks, History, Search), which are implemented as "tabs". When they pick an article from any tab they jump into article reading mode (the detail view). Second, I'm using .navigationBarBackButtonHidden(true) to remove the auto back button that pops back to the sidebar view. This button does still work in iOS 26.4, even with the nested TabView. However, I can't use the auto back button because my detail view is actually a WebView with its own back/forward logic and UI. Therefore, I need a separate close button to exit from the detail view. My close button sets selectedArticle to nil, which (pre-iOS 26.4) would trigger the NavigationSplitView to pop back to the sidebar view. For some reason, in iOS 26.4 the NavigationSplitView doesn't seem to bind correctly to the List's selection parameter, specifically when there's a TabView nested between them. Or, rather, it binds, but fails to pop back when selection becomes nil. One option is to replace NavigationSplitView with NavigationStack (on iPhone). NavigationStack still works with a nested TabView, but it creates other downstream issues for me (as well as forcing me to branch for iPhone and iPad), so I'd prefer to continue using NavigationSplitView. Does anyone have any ideas about how to work around this problem? Is there some way of explicitly telling NavigationSplitView to pop back to the sidebar view on iPhone? (I've tried setting the column visibility but nothing seems to work). Thanks for any help!
Replies
0
Boosts
0
Views
33
Activity
6d
Localization in Swift macOS console Apps.
Is it possible to build localization into console apps, developed in SwiftUI in Xcode26. I have created a catalog, (.xcstrings file) with an English and fr-CA string. I have tried to display the French text without success. I am using the console app to test a package which also has English/French text. English text works fine in both package and the console main, but I cannot generate the French. From what I can discover so far it's not possible without bundling it as a .app, (console app). Looking for anyone who has crossed this bridge.
Replies
7
Boosts
0
Views
277
Activity
3d
UI Glitch in Toolbar Menu Picker After Migrating to Xcode 26
I am experiencing a UI issue after migrating my app from Xcode 16 to Xcode 26. In my implementation, I have a toolbar that contains multiple buttons along with a dropdown menu. The hierarchy for dropdown is as follows: **Toolbar → ToolbarItem → View → Menu → Picker ** Prior to Xcode 26, this setup worked smoothly in production builds. The dropdown (Menu + Picker) behaves as expected, and selecting a value triggers loading a dataset containing thousands of records on the screen. However, after upgrading to Xcode 26, I am observing an animation glitch when dismissing the dropdown after a selection is made. Specifically, the dropdown briefly shows a “capsule-like” animation artifact during dismissal, which persists for a few seconds. This visual issue is noticeable and negatively impacts the perceived performance and user experience of the app. This issue is occurring in an already released app built with Xcode 26. Questions: Is this a known issue or regression in Xcode 26 / SwiftUI Menu or Picker components? If yes, are there any known fixes or upcoming Xcode versions where this is resolved? If not, what would be the recommended approach to eliminate or minimize this animation glitch when dismissing the dropdown? Additional Context: The issue appears only after migration to Xcode 26. The dataset loaded after selection is large (thousands of records). The glitch specifically occurs during the dismissal animation of the Menu/Picker. Any guidance or workarounds would be greatly appreciated.
Replies
1
Boosts
0
Views
39
Activity
4d
Previews for SwiftUI views in Packages don't work in Xcode 26.4
I have an iOS project based on SwiftUI in which almost all code is organised in Packages. With Xcode 26.2 and 26.3, I can preview all SwiftUI views without issues. With Xcode 26.4, the same previews don't work, in the canvas appears this error message: "Cannot preview in this file. Could not find target description for “TaskListView.swift”". The explanation is: "The list of source files that produce object files did not contain this file to be previewed. Check to make sure it is not excluded using the EXCLUDED_SOURCE_FILE_NAMES build setting." If I add a SwiftUI view to the main project files (not in a package), the preview works as expected. Is it an Xcode 26.4 regression? Or do I need to modify some configuration file?
Replies
5
Boosts
0
Views
169
Activity
3d
onDisappear not called when closing a document on macOS (Designed for iPad), works on iPad
When running a SwiftUI DocumentGroup app on macOS designed for iPad, onDisappear is not called when closing a document, and deinit of state objects owned by a ContentView is not invoked. This behavior works as expected on iPad. @main struct MyApp: App { var body: some Scene { DocumentGroup(newDocument: MyDocument()) { file in ContentView(document: file.$document) .onDisappear { print("This isn't called on macOS Designed For iPad, but is on iPad when closing a document.") } } } } It is my understanding that for a macOS designed for iPad these lifecycle events would behave the same - otherwise there appears to be no way to detect if a document has closed on macOS.
Replies
1
Boosts
0
Views
75
Activity
1w
How to load and draw texture with opacity in Metal
The background I'm finally working to convert my very old Mac kaleidoscope application, ScopeWorks, which was written in OpenGL and Objective-C, to a Multiplatform app in SwiftUI and Metal. I'm using the MetalKit MTKView class, wrapped for SwiftUI as an NSViewRepresentable or UIViewRepresentable. I then provide an MTKViewDelegate that provides a draw method. The draw method fetches the current render pass descriptor, creates a command buffer, sets up a render pipeline, and does its drawing. My renderer's makePipeline method looks like this: func makePipeline() { let library = device.makeDefaultLibrary() let pipelineDesc = MTLRenderPipelineDescriptor() pipelineDesc.vertexFunction = library?.makeFunction(name: "vertex_main") pipelineDesc.fragmentFunction = library?.makeFunction(name: "fragment_main") pipelineDesc.colorAttachments[0].pixelFormat = .bgra8Unorm pipeline = try! device.makeRenderPipelineState(descriptor: pipelineDesc) } And my shaders look like this: struct VertexOut { float4 position [[position]]; float2 texCoord; }; vertex VertexOut vertex_main(const device float2* position [[buffer(0)]], uint vid [[vertex_id]]) { VertexOut out; float2 pos = position[vid]; out.position = float4(pos, 0, 1); out.texCoord = pos * 0.5 + 0.5; // basic mapping return out; } fragment float4 fragment_main(VertexOut in [[stage_in]], texture2d<float> tex [[texture(0)]], constant float4& color [[buffer(1)]]) { constexpr sampler s(address::repeat, filter::linear); // float4 texColor = tex.sample(s, in.texCoord); // return texColor * color; float4 textureColor = {1, 2, 3, 4}; if (all(color == textureColor)) { return tex.sample(s, in.texCoord); } else { return color; } // Sample the texture directly — no color tint applied return tex.sample(s, in.texCoord); } The first part of my MTKViewDelegate's draw method looks like this: func draw(in view: MTKView) { guard let drawable = view.currentDrawable, let descriptor = view.currentRenderPassDescriptor, let pipeline = pipeline, let texture = texture else { return } let commandBuffer = commandQueue.makeCommandBuffer()! let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor)! encoder.setRenderPipelineState(pipeline) encoder.setFragmentTexture(texture, index: 0) descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0, blue: 0, alpha: 1.0) // Draw six equilateral triangles forming the hexagon let radius: Float = 0.6 for i in 0..<6 { let angle = Float(i) * (.pi / 3) let cosA = cos(angle) let sinA = sin(angle) let nextA = Float(i+1) * (.pi / 3) let cosB = cos(nextA) let sinB = sin(nextA) let verts: [simd_float2] = [ simd_float2(0, 0), simd_float2(radius * cosA, radius * sinA), simd_float2(radius * cosB, radius * sinB) ] encoder.setVertexBytes(verts, length: MemoryLayout<simd_float2>.stride * 3, index: 0) // Tell the fragment shader to use the texture color. var textureColor: simd_float4 = simd_float4(1, 2, 3, 4) encoder.setFragmentBytes(&textureColor, length: MemoryLayout<SIMD4<Float>>.stride, index: 1) encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3) One of the things the existing app does is load PNG or TIFF images with an alpha channel, and then overlay parts of the image on top of themselves flipped, so you get interesting Moiré patterns in the lines in the resulting kaleidoscope. For now I'm working on a single sample image, loading it into a texture in Metal, and just rendering it as a hexagon and drawing lines for the triangles that make up the hexagon. (For now I'm using the vertex coordinates as the texture coordinates, so I get a hexagonal part of my texture rather than a single triangular part tessellated into a hexagon. I'll fix that later.) In both iOS and OS I set the clear color to black at the beginning of the draw function. The issue: The source image is mostly transparent, but with a lot of partly transparent pixels. Here's what it looks like in Photoshop, where you can see the transparent parts as a checkerboard pattern: (I tried to crop the original image to show the approximate part that I'm rendering in a hexagon, but it's not exact. Look for the same shapes in the different images to compare them.) When I render my hexagon in the Metal view in the iOS version of the app, it looks like it's forcing each pixel to fully opaque or fully transparent: And in the macOS version of the app, it seems to force ALL the pixels to opaque: I haven't shown all the setup code, because it's' a lot. Is there some rendering mode setup I'm missing in order to get it to draw the pixels into the output based on their opacity, including partial opacity?
Replies
2
Boosts
0
Views
764
Activity
6d
Unwanted animation of navbar controls
What could cause the issue shown on the gif. At first I though clean build folder helps. But when you close the main window and open it after some time it gets back to this state. The whole set of elements in the navbar starts shifting to the right and it continues infinitely 15.6.1 (24G90) Swift 6.1.2
Replies
2
Boosts
0
Views
74
Activity
1w
Restoring most recent document at cold start in DocumentGroup iOS app?
I've tried everything I can to restore the most recent document at cold start in my DocumentGroup iOS app. Q1. I believe it's not possible, but I would be happy to be proven wrong? Q2. Why is this not possible? My users who only edit one document find it quite annoying to have to select it so frequently.
Replies
0
Boosts
0
Views
46
Activity
1w
Section(isExpanded:) in sidebar List, inconsistent row animation on collapse/expand
When using Section(_:isExpanded:) inside a List with .listStyle(.sidebar) in a NavigationSplitView, some rows don't animate with the others during collapse and expand. Specific rows (often in the middle of the section) snap in/out instantly while the rest animate smoothly. I've reproduced this with both static views and ForEach. Minimal reproduction: struct SidebarView: View { @State private var sectionExpanded = true @State private var selection: Int? var body: some View { NavigationSplitView { List(selection: $selection) { Section("Section", isExpanded: $sectionExpanded) { ForEach(1...3, id: \.self) { index in NavigationLink(value: index) { Label("Item \(index)", systemImage: "\(index).circle") } } } } .listStyle(.sidebar) .navigationTitle("Sidebar") } detail: { if let selection { Text("Selected item \(selection)") } else { Text("Select an item") } } } } Environment: macOS 26.3, Xcode 26.3, SwiftUI Steps to reproduce: Run the above code in a macOS app Click the section disclosure chevron to collapse Observe that some rows animate out while others snap instantly Expand again — same inconsistency Expected: All rows animate together uniformly. Actual: Some rows (typically middle items) skip the animation entirely. I also tried using static Label views instead of ForEach, same result. Is there a known workaround?
Replies
2
Boosts
0
Views
191
Activity
1w
iOS 26: Toolbar button background flashes black during NavigationStack transitions (dark mode)
I’m seeing a visual glitch with toolbar buttons when building with Xcode 26 for iOS 26. During transitions (both pushing in a NavigationStack and presenting a .sheet with its own NavigationStack), the toolbar button briefly flashes the wrong background colour (black in dark mode, white in light mode) before animating to the correct Liquid Glass appearance. This happens even in a minimal example and only seems to affect system toolbar buttons. A custom view with .glassEffect() doesn’t have the issue. I’ve tried: .tint(...), UINavigationBarAppearance/UIToolbarAppearance, and setting backgrounds on hosting/nav/window but none of those made any difference. Here’s a minimal reproducible example: import SwiftUI struct ContentView: View { @State private var showingSheet = false var body: some View { NavigationStack { List { NavigationLink("Push (same stack — morphs)") { DetailView() } Button("Sheet (separate stack — flashes)") { showingSheet = true } } .navigationTitle("Root") .scrollContentBackground(.hidden) .background(.gray) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Action") {} } } .sheet(isPresented: $showingSheet) { SheetView() } } } } struct DetailView: View { var body: some View { Text("Detail (same stack)") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.gray) .navigationTitle("Detail") .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Action") {} } } } } struct SheetView: View { var body: some View { NavigationStack { Text("Sheet (separate stack)") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.gray) .navigationTitle("Sheet") .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Action") {} } } } } } Has anyone else seen this or found a workaround outside of disabling this background completely with .sharedBackgroundVisibility(.hidden)? I have filed a bug report under FB22141183
Replies
1
Boosts
0
Views
316
Activity
1w
Now Available: Wishlist Sample Code for SwiftUI
We’ve just added a new sample code project to the SwiftUI Essentials documentation! If you attended the recent SwiftUI foundations: Build great apps with SwiftUI activity, you might recognize Wishlist, our travel-planning sample app. You can now explore and download the complete project here
Replies
0
Boosts
3
Views
54
Activity
1w
Glass Effect Label Shadow Clipping During Morph Animation
Hi all, I’m experiencing a visual bug when applying the glass effect to a Label in Liquid Glass (current version 26.2 on simulator; also reproducible in 26.3.1 on device). Issue: On a label with .glassEffect(.regular), when collapsing via morph animation, the shadow is clipped during the animation, and then suddenly "pops" back to its un-clipped state, resulting in a jarring visual effect. Minimal Example: import SwiftUI struct ContentView: View { var body: some View { Menu { Button("Duplicate", action: {}) Button("Rename", action: {}) Button("Delete…", action: {}) } label: { Label("PDF", systemImage: "doc.fill") .padding() .glassEffect(.regular) } } } #Preview { ContentView() } I am not sure if I am misusing the .glassEffect() on the label and maybe there is another more native way of achieving this look? Any advice or workaround suggestions would be greatly appreciated!
Replies
2
Boosts
0
Views
93
Activity
1w
PhaseAnimator doesn't reflect @Observable state changes after animation settles
I ran into a behavior with PhaseAnimator that I'm not sure is a bug or by design. I'd appreciate any insight. The Problem When an @Observable property is read only inside a PhaseAnimator content closure, changes to that property are ignored after the animation cycle completes and reaches its resting state. The UI gets stuck showing stale data. Minimal Reproduction I've put together a simple demo with two views side by side, both driven by the same ViewModel and toggled by the same button: BrokenView — receives an @Observable object and reads its property inside PhaseAnimator. After the animation completes, toggling the property has no visible effect. FixedView — receives the same value as a Bool parameter. Updates correctly every time because view's parameter has changed. import SwiftUI @Observable class ViewModel { var isError = false } struct BrokenView: View { let viewModel: ViewModel @State private var trigger = false var body: some View { VStack(spacing: 20) { Text("Broken (@Observable)").font(.headline) PhaseAnimator([false, true], trigger: trigger) { _ in if viewModel.isError { Text("Error!").foregroundStyle(.red).font(.largeTitle) } else { Text("OK").foregroundStyle(.green).font(.largeTitle) } } } .padding() .onAppear { trigger = true } } } struct FixedView: View { let isError: Bool @State private var trigger = false var body: some View { VStack(spacing: 20) { Text("Fixed (Value Type)").font(.headline) PhaseAnimator([false, true], trigger: trigger) { _ in if isError { Text("Error!").foregroundStyle(.red).font(.largeTitle) } else { Text("OK").foregroundStyle(.green).font(.largeTitle) } } } .padding() .onAppear { trigger = true } } } struct DemoView: View { @State private var viewModel = ViewModel() var body: some View { VStack(spacing: 40) { BrokenView(viewModel: viewModel) Divider() FixedView(isError: viewModel.isError) Divider() Button("Toggle isError: \(viewModel.isError)") { viewModel.isError.toggle() } .buttonStyle(.borderedProminent) } .padding() } } Run the preview, then tap the toggle button. FixedView updates instantly; BrokenView stays stuck. My Understanding It seems like PhaseAnimator only tracks @Observable access during active animation phases. Once it settles at rest, the content closure is not re-evaluated, so observation tracking is effectively lost. Passing a value type works because SwiftUI view diffing detects the input change and triggers a body re-evaluation, which in turn re-evaluates the PhaseAnimator content. Question Is this intended behavior? Or shouldn't I use phase animator in this way? I could not find any mention of this limitation in the documentation. If it is by design, it might be worth documenting — it is a subtle pitfall that is easy to miss. Thanks in advance for any input!
Replies
0
Boosts
0
Views
101
Activity
2w
How do you support Preferred Font Size / Dynamic Type on macOS?
On macOS 26, how do you support the Preferred Text Size value as defined in the Accessibility Settings? Historically, "Dynamic Type" has not been available on macOS. However, the user has some control over text size through the Accessibility Settings. On macOS 26, a small subset of applications are honouring changes to that value include Finder, Mail, and sidebars in many applications. Dynamic sizing in table views has been available on macOS for awhile. But Mail.app, in particular, is also adjusting the font sizes used in the message's body pane while the Finder is adjusting font sizes used for Desktop icons. I can't find an NSNotification that is fired when the user adjusts the Accessibility Text Size slider, nor can I find an API to read the current value. NSFont.preferredFont(forTextStyle:options:) looks promising but the fonts returned do not appear to take the user's Accessibility setting into account. (Nor do they update dynamically.) SwiftUI's Text("Apple").font(.body) performs similarly to NSFont in that it does respect the style, but it does not honour dynamic sizing. NSFontDescriptor has a bunch of interesting methods, but none that seem to apply to Accessibility Text Size. Given an AppKit label: let label = NSTextField(labelWithString: "AppKit") label.font = NSFont.preferredFont(forTextStyle: .body) Or a SwiftUI label: Text("SwiftUI").font(.body) How do I make either of them responsive to the user's Text Size setting under Accessibility? Note this is on macOS 26 / Xcode 26. I realize there have been some previous forum posts related to this issue but hoping that things might have improved since then.
Replies
2
Boosts
0
Views
445
Activity
2w
Back gesture not disabled with navigationBarBackButtonHidden(true) when using .zoom transition
[Submitted as FB22226720] For a NavigationStack destination, applying .navigationBarBackButtonHidden(true) hides the back button and also disables the interactive left-edge back gesture when using the standard push navigation transition. However, when the destination uses .navigationTransition(.zoom), the back button is hidden but the left-edge back gesture is still available—it can still be dismissed even though back is intentionally suppressed. This creates inconsistent behavior between navigation transition styles. navigationBarBackButtonHidden(_:) works with a standard push transition, but not with .navigationTransition(.zoom). In the code below, .interactiveDismissDisabled(true) is also applied as another attempt to suppress the back-swipe gesture, but it has no effect. As a result, there’s currently no clean way to prevent back navigation when using the zoom transition. REPRO STEPS Create an iOS project then replace ContentView with code below, build and run. Leave nav type set to List Push. Open an item. Verify there is no back button, then try the left-edge back gesture. Return to the root view. Change nav type to Grid Zoom. Open an item. Verify there is no back button, then try the left-edge back gesture. ACTUAL In List Push mode, the left-edge back gesture is prevented. In Grid Zoom mode, the back button is hidden, but the left-edge back gesture still works and returns to the previous view. EXPECTED Behavior should be consistent across navigation transition styles. If this configuration is meant to suppress interactive backward navigation for a destination, it should also suppress the left-edge back gesture when using .navigationTransition(.zoom). SCREEN RECORDING SAMPLE CODE struct ContentView: View { private enum NavigationMode: String, CaseIterable { case listPush = "List Push" case gridZoom = "Grid Zoom" } @Namespace private var namespace @State private var navigationMode: NavigationMode = .listPush private let colors: [Color] = [.red, .blue] var body: some View { NavigationStack { VStack(spacing: 16) { Picker("Navigation Type", selection: $navigationMode) { ForEach(NavigationMode.allCases, id: \.self) { mode in Text(mode.rawValue).tag(mode) } } .pickerStyle(.segmented) if navigationMode == .gridZoom { HStack { ForEach(colors.indices, id: \.self) { index in NavigationLink(value: index) { VStack { RoundedRectangle(cornerRadius: 14) .fill(colors[index]) .frame(height: 120) Text("Grid Item \(index + 1)") .font(.subheadline.weight(.medium)) } .padding(12) .frame(maxWidth: .infinity) .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 16)) .matchedTransitionSource(id: index, in: namespace) } .buttonStyle(.plain) } } } else { ForEach(colors.indices, id: \.self) { index in NavigationLink(value: index) { HStack { Circle() .fill(colors[index]) .frame(width: 24, height: 24) Text("List Item \(index + 1)") Spacer() Image(systemName: "chevron.right") .foregroundStyle(.secondary) } .padding() .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 12)) } .buttonStyle(.plain) } } Spacer() } .padding(20) .navigationTitle("Prevent Back Swipe") .navigationSubtitle("Compare Grid Zoom vs List Push") .navigationDestination(for: Int.self) { index in if navigationMode == .gridZoom { DetailView(color: colors[index]) .navigationTransition(.zoom(sourceID: index, in: namespace)) } else { DetailView(color: colors[index]) } } } } } private struct DetailView: View { @Environment(\.dismiss) private var dismiss let color: Color var body: some View { ZStack { color.ignoresSafeArea() Text("Try left-edge swipe back") .font(.title.bold()) .multilineTextAlignment(.center) .padding(.horizontal, 24) } .navigationBarBackButtonHidden(true) .interactiveDismissDisabled(true) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Close", action: dismiss.callAsFunction) } } } }
Replies
2
Boosts
0
Views
605
Activity
1w