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

SwiftUI Documentation

Posts under SwiftUI subtopic

Post

Replies

Boosts

Views

Activity

TabView + NavigationStacks broken on iOS 26.1
Broken TabView + NavigationStacks functionality on iOS 26.1. Even the most basic navigation made impossible. Example: TabView { Tab(...) { NavigationStack(path: $homePath) { HomeView() .navigationDestination { ... } } } Tab(...) { NavigationStack(path: $settingsPath) { SettingsView() .navigationDestination { ... } } } } Anything passed to settingsPath is just ignored and would never appear onscreen. (26.0 and prior versions work as expected.) Are there any workarounds?
1
1
106
Nov ’25
NavigationStack Back Button obscured in MacOS
A MacOS SwiftUI app using WindowGroup with a NavigationStack results in having the navigation title overlay the back button. Note that this does not occur if a tabbed interface is used. The work around is simply to hide the navigation bar back button and add a tool bar item that is custom back button. I found a fix on this forum, and it was similar to: #if os(macOS) .navigationBarBackButtonHidden(true) .toolbar { ToolbarItem(placement: .navigation) { Button(action: { dismiss() }) { Label("Back", systemImage: "arrow.left.circle") } } } #endif modifying the NavigationLink targets and where the dismiss() function was provided by: @Environment(\.dismiss) private var dismiss Is there any means to sign up for a notification to inform me that this bug has been fixed?
Topic: UI Frameworks SubTopic: SwiftUI
2
0
133
Nov ’25
Adopt watchface background for complication
On the California watchface (white) there are two complication slots. The upper one is seemingly not a standard slot and limited to a few Apple-owned apps (Calender, Time, …). It adopts the (default white) background of the watchface, so the date is neatly and cleanly displayed on the watchface backdrop. The other lower circular complication makes up for a fat black bubble now on the clean face, which doesn‘t look too pretty (of course depending on the complication…). I would like to create a complication with rather minimalistic content, and it would look great if it could also share the white background and just produce that content on top of it. While the documentation sounds like it would be possible to make the widget background adopt the context colors (which I would understand as using the same background color), for the life of me I can‘t get anything else than black for the circle. Has anyone achieved that? How would I do that? (Image below shows the temperature widget…mine would have way less and smaller content….)
0
0
92
Nov ’25
proxy.ScrollTo Unitpoint Y Value uses negative tenths for positioning?
I am using a ScrollViewReader, ScrollView, LazyVStack to organize a list of elements I want to be able to scroll to a specific location so i use elementID in the list and a UnitPoint value. But the y value for unitpoint uses -0.1 to represent 100%. Is this intended behavior, a bug, or am i using something incorrectly? I could not find this in the docs and it was only after debugging I found that proxy.scrollTo(id, UnitPoint(x:0, y:-0.1)) is how you scroll to the end of an item or proxy.scrollTo(id, UnitPoint(x:0, y:-0.05)) to scroll to the center. Am I accessing the scrollTo property wrong or is this just how we use it? Also it seems .bottom is broken due to this aswell (as it uses 1 but the actual value that scrolls to the bottom is -0.1). This seems like unintended behvaior as UnitPoints are supposed to be have values of 0-1 Here is a view which reproduces this behavior struct ScrollTestView: View { @State private var scrollToId: String = "" @State private var scrollToAnchorY: String = "0.0" @State private var scrollProxy: ScrollViewProxy? var body: some View { VStack { HStack(spacing: 12) { TextField("Enter ID (1-30)", text: $scrollToId) .frame(width: 120) .padding(8) .background(Color.gray.opacity(0.1)) .cornerRadius(8) TextField("Anchor Y (0-1)", text: $scrollToAnchorY) .frame(width: 120) .padding(8) .background(Color.gray.opacity(0.1)) .cornerRadius(8) Button { guard let targetId = Int(scrollToId), let anchorY = Double(scrollToAnchorY), let proxy = scrollProxy else { return } let anchorPoint = UnitPoint(x: 0.5, y: anchorY) proxy.scrollTo(targetId, anchor: anchorPoint) } label: { Text("Scroll") .font(.subheadline) .padding(.horizontal, 16) .padding(.vertical, 8) .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } Spacer() } .padding(.horizontal, 16) .padding(.vertical, 8) ScrollViewReader { proxy in ScrollView { LazyVStack { ForEach(1...30, id: \.self) { itemId in VStack { HStack { Text("Item \(itemId)") .font(.title2) .bold() Spacer() } .padding(.vertical, 16) Divider() .background(Color.gray.opacity(0.6)) } .id(itemId) } } .padding() } .onAppear { scrollProxy = proxy } } } } }
0
0
52
Nov ’25
NSButton + TtGC6AppKit18_NSCoreHostingViewVS_12AppKitButton - Image Alignment Changed/Broken in Minor macOS 26.1 Update
I just updated to macOS 26.1. I have a pure AppKit app (I guess that's not possible anymore but as close to a pure AppKit app as you can get). I use NSButton with the glass bezel style and SF symbol images. It looks like the minor OS update brought layout changes because now some of these buttons are scaling the symbol image much larger than was being done on macOS 26. The image can sometimes draw outside the glass 'bezel'. It looks like using the 'info' symbol in a button results in much larger image scaling than it did on the previous Tahoe for some SF Symbols. With the glass bezel style and a SF Symbol image how am I supposed to consistently make the button look good? With certain symbols I have to use imageScaling NSImageScaleProportionallyUpOrDown and on others I have to use NSImageScaleProportionallyDown. If I'm using a system image shouldn't it just do the right thing? Is trial and error the only way to tell? That's what I was doing before but it seems that the minor 26.1 update changed things. Additionally calling -sizeToFit on a button multiple times can cause it to shrink for example: [button sizeToFit]; // <-- At fitting size // Then later [button sizeToFit]; // Now button is shrunk But if the button is already at its fitting size an additional call later shouldn't make it shrink, it should stay the same size. FB20517174 I see I now inherit SwiftUI, not sure if that has anything to do with this but if I wanted to opt in to fragile layout I wouldn't be using Appkit...
1
0
77
Nov ’25
Modal presentation of SwiftUI view with TextField leads to frozen UI, missing keyboard and memory leak
Hello, I’m trying to present my custom SwiftUI dialog with text field in UIKit with modalPresentationStyle = .overFullScreen, but it leads to the UI being completely frozen once I select the TextField and memory constantly leaking. The minimal reproducible code is: class ModalBugViewController: UIViewController { var hostingController: UIHostingController<Content>! struct Content: View { @State private var text = "" var body: some View { ZStack { Color.black.opacity(0.5).ignoresSafeArea() VStack { TextField("Test", text: $text) .textFieldStyle(.roundedBorder) .padding() } } } } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .clear hostingController = UIHostingController(rootView: Content()) addChild(hostingController) view.addSubview(hostingController.view) hostingController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: view.topAnchor), hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) ]) hostingController.didMove(toParent: self) } } And then in UIKit source view: let viewController = ModalBugViewController() viewController.modalPresentationStyle = .overFullScreen present(viewController, animated: true) The bug is reproducible on iOS 18 - 26.1, even on the simulator, although on iOS 26 it's in landscape mode only. Is there some workaround for this issue that doesn't involve rewriting the whole dialog in UIKit?
1
0
106
Nov ’25
Bar button item showing wrong overlay after pop transition on iOS 26.1
I am experiencing a frustrating bug on iOS 26.1 that makes my app look as if it lacks attention to detail. Basically, when having a NavigationStack, where the root view has a top-right confirmation bar button item, and a pushed detail view does not have any button in the navigation bar trailing position, upon poping back to the root view, the confirmation bar button item shows a white overlay for about 1-2 seconds before finally disappearing and showing the correct appearance. Here is the incorrect appearance right after the pop transition: Eventually after about 1,5 seconds it gets turned back to what it should look like: Here is the full code that you can use to reliably reproduce this issue on iOS 26.1: @State private var path: [Int] = [] var body: some View { NavigationStack(path: $path) { VStack { Text("First View") .font(.title) } .navigationDestination(for: Int.self, destination: { param in Text("Detail View") .font(.title) }) .navigationTitle("First") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItemGroup(placement: .confirmationAction) { Button("Next", role: .confirm, action: { self.path.append(1) }) } } } } } I submitted a Feedback for it: FB21010613 . Is there anything at all I can do on my end to work around this issue?
0
0
72
Nov ’25
iOS Dark mode Liquid Glass Tab bar with Reduce Transparency Issue
Hi, I have an iOS app that I’m trying to update with Liquid Glass. In this app, I’m using a tab bar, which works fine with Liquid Glass, but as soon as I enable the “Reduce Transparency” setting in dark mode, I get a strange effect: at launch, the tab bar appears correctly in dark mode, but after scrolling a bit in the view, it eventually switches to light mode 😅 At launch: After a bit of scrolling: I can’t figure out whether this is intended behavior from the framework or not (I don’t have this issue with other apps). I can reproduce it in a project built from scratch, here is the code (don't forget to set dark mode to the device and activate the reduce transparency option in the accessibility menu): struct ContentView: View { var body: some View { TabView { ScrollView { LazyVStack { ForEach(0..<100) { _ in Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello world").foregroundStyle(.primary) } } .padding() } .tabItem { Label("Menu", systemImage: "list.dash") } } } } Do you know if this is expected behavior? Or if there’s something that can be done about it? Thanks,
1
0
107
Nov ’25
watchos 26
In the latest version of Watchos 26, an issue has been discovered with the following symptoms: Placing a button on an overlay page, buttonStyle(PlainButtonStyle()), Long press and slide, the button can actually slide and return to its original position. Previously, watchos did not have this problem. Can experts take a look
Topic: UI Frameworks SubTopic: SwiftUI
0
0
30
Nov ’25
SwiftUI navigationTransition Regression on iOS 26 (Source View Disappears + Flicker/Misaligned Geometry)
Summary I’m experiencing two issues with SwiftUI’s navigationTransition(.zoom) on iOS 26.0 and 26.1 that break previously smooth transitions. These issues appear both on real devices and Simulator. The same code works correctly on iOS 18. Issue 1 - Source View Disappears After Drag-Dismiss When using .navigationTransition(.zoom(sourceID:..., in:...)), the source view disappears completely after the transition finishes. This only happens when the detail view is dismissed via drag (interactive dismiss). When the view is dismissed by tapping the back button, the source view remains visible as expected. Reproduced on: iOS 26.0, iOS 26.0.1 (17A400), iOS 26.1 (Simulator + physical device) Issue 2 — Flickering and Geometry Mismatch During Transition Compared to iOS 18 behavior, the outgoing view and incoming view no longer share consistent geometry. Current behavior on iOS 26: The disappearing view flickers during the drag-dismiss interaction. The source and destination views no longer align geometrically. Instead of smoothly morphing as in previous iOS versions, the two views briefly overlap incorrectly before applying the zoom animation. Expected (iOS 18) behavior: Matched geometry between source and destination. Smooth, stable zoom transition with no flickering. // // ContentView.swift // DummyTransition // // Created by Sasha Morozov on 12/11/25. // import SwiftUI struct RectItem: Identifiable, Hashable { let id: UUID = UUID() let title: String let color: Color } struct ContentView: View { @Namespace private var zoomNamespace private let items: [RectItem] = [ RectItem(title: "Red card", color: .red), RectItem(title: "Blue card", color: .blue), RectItem(title: "Green card", color: .green), RectItem(title: "Orange card", color: .orange) ] var body: some View { NavigationStack { ScrollView { VStack(spacing: 16) { ForEach(items) { item in NavigationLink { DetailView(item: item, namespace: zoomNamespace) .navigationTransition( .zoom(sourceID: item.id, in: zoomNamespace) ) } label: { RoundedRectangle(cornerRadius: 16) .fill(item.color.gradient) .frame(height: 120) .overlay( Text(item.title) .font(.headline) .foregroundStyle(.white) ) .padding(.horizontal, 16) .matchedTransitionSource(id: item.id, in: zoomNamespace) } .buttonStyle(.plain) } } .padding(.vertical, 20) } .navigationTitle("Cards") } } } struct DetailView: View { let item: RectItem let namespace: Namespace.ID var body: some View { ZStack { item.color Text(item.title) .font(.largeTitle.bold()) .foregroundStyle(.white) } .ignoresSafeArea() .navigationTitle("Detail") .navigationBarTitleDisplayMode(.inline) } } #Preview { ContentView() } Testing Environment MacBook Pro (2023, M2 Pro, 16 GB RAM) macOS 26.2 Beta (25C5031i) Xcode: Version 26.0.1 (17A400) Devices tested: Simulator (iOS 26.0 / 26.1) Physical device (iPhone 16) running iOS 26.1
Topic: UI Frameworks SubTopic: SwiftUI
1
1
156
Nov ’25
refreshable modifier causes misaligned button tap targets in ScrollView
I have a ScrollView with several Buttons and a .refreshable modifier. As soon as I pull to refresh and the refresh indicator appears, the tap targets no longer match the visible button positions. For example, tapping button A triggers button B’s action, as if the hit-testing region didn’t move along with the content. This only happens while the refresh indicator is shown. Before pulling to refresh, everything is correct and afterwards as well. Tested on iOS 18 and 26 (Xcode 16.4, Xcode 26). Here is a minimal reproducible example: import SwiftUI struct ContentView: View { var body: some View { ScrollView { VStack { Button("Button A") { print("A") } .buttonStyle(.borderedProminent) Button("Button B") { print("B") } .buttonStyle(.borderedProminent) Button("Button C") { print("C") } .buttonStyle(.borderedProminent) Button("Button D") { print("D") } .buttonStyle(.borderedProminent) Button("Button E") { print("E") } .buttonStyle(.borderedProminent) } .frame(maxWidth: .infinity) } .refreshable { try? await Task.sleep(for: .seconds(60)) } } }
2
0
91
Nov ’25
Correctly initializing observable classes in modern SwiftUI
I have two @Observable manager classes, which share a reference to a third class. I initialize this setup using a custom init in my App struct, like so: @main struct MyApp: App { private let managerA: ManagerA private let managerB: ManagerB init() { let managerC = ManagerC() self.managerA = ManagerA(managerC: managerC) self.managerB = ManagerB(managerC: managerC) } var body: some Scene { WindowGroup { ContentView() .environment(managerA) .environment(managerB) } } } I've been using this pattern for some time and it has been working fine. However, I just today discovered that @Observable objects are supposed to be initialized as @State vars, as shown in Apple's documentation here. This means I shoud be doing the following: @main struct MyApp: App { @State private var managerA: ManagerA @State private var managerB: ManagerB init() { let managerC = ManagerC() self.managerA = ManagerA(managerC: managerC) self.managerB = ManagerB(managerC: managerC) } var body: some Scene { WindowGroup { ContentView() .environment(managerA) .environment(managerB) } } } I've also seen some examples where the @State vars are initialized manually like this: @main struct MyApp: App { @State private var managerA: ManagerA @State private var managerB: ManagerB init() { let managerC = ManagerC() let managerA = ManagerA(managerC: managerC) let managerB = ManagerB(managerC: managerC) self._managerA = State(initialValue: managerA) self._managerB = State(initialValue: managerB) } var body: some Scene { WindowGroup { ContentView() .environment(managerA) .environment(managerB) } } } ChatGPT tells me the third approach is the correct one, but I don't understand why and ChatGPT can't produce a convincing explanation. The compiler doesn't produce any errors or warnings under each approach, and as far as I can tell, they all behave identically with no discernible difference in performance. Does it matter which pattern I use? Is there a "correct" way?
2
0
69
Nov ’25
Recommended approach for migrating to modern (iOS 16+) navigation
Hello all, my team and I are looking for some advice on updating our app from using NavigationView and NavigationLink to NavigationStack and .navigationDestination now that NavigationView is deprecated. A little background about our situation, our app is a mix of SwiftUI views and UIKit view controllers. We are slowly migrating towards primarily SwiftUI and we are at the point now where our main app entry point is SwiftUI. UIKit is now mainly just used for some legacy screens inside the app, but majority of our navigation is using SwiftUI with NavigationLinks. I have spent a couple weeks on trying to migrate to using NavigationStack + .navigationDestination, but every time I do I keep running into issues. From what I understand, there seems to be two competing approaches for modern navigation. Those two approaches are... Having a more global navigationDestination modifier defined at the root of each tab that essentially supports navigating to all pages. I have seen this referred to as a 'router'. Applying a navigationDestination modifier on each page that navigates somewhere. This seems to be more 1-to-1 port of how we are currently using NavigationLink. However, I tried implementing both of these solutions in our app and with both of them I ran into countless issues which made me second guess the solution I was currently implementing in favor of the other. This has led to where I am now, where I am really unsure what the recommended approach is. I would love to hear from you all, what you have had the most success with. I am interested to hear what approach you chose, why you chose that, and then also some downsides to the approach you chose. Thanks in advance.
Topic: UI Frameworks SubTopic: SwiftUI
2
0
66
Nov ’25
NavigationSplitView and NavigationStack persistence
Let's say you have a NavigationModel that contains three NavigationPaths, one for each option in the sidebar for a NavigationSplitView. This NavigationModel is created by the App and passed down to the root view for the scene in its environment. Each option has a separate NavigationStack and is passed a binding to the appropriate NavigationPath from the NavigationModel. Is it expected that when the user changes the selection in the sidebar, the NavigationPath for the newly selected view should be erased? This is what's currently happening on macOS 26. It seems like the default action when creating a NavigationStack and passing it a binding to a NavigationPath is to clear that path and start from the root view. Is this normal, intended behaviour or is it a bug? Or, perhaps, an option or modifier I am missing?
3
0
215
Nov ’25
`NewDocumentButton(contentType:)` gives "Content serialization failed, document won't be saved."
I'm working on an iOS document-based app. It uses ReferenceFileDocument and custom creation of documents via DocumentGroupLaunchScene + NewDocumentButton. It works fine when I use the plain NewDocumentButton("Whatever") (without any more arguments), but when I want to perform additional setup via preapreDocumentURL or even just add a contentType it gives such output in the console when I hit it: Content serialization failed, document won't be saved. UTType.replayable is correctly wired up in the plist. It looks like a bug in the SDK, but maybe there is a chance that I'm doing something wrong? Here's a code: import SwiftUI import UniformTypeIdentifiers import Combine @main struct MyApp: App { var body: some Scene { DocumentGroup { Document() } editor: { documentConfiguration in EmptyView() } DocumentGroupLaunchScene("Yoyo") { NewDocumentButton(contentType: .replayable) { return URL(string: "whatever, it doesnt even go there...")! } } } } final class Document: ReferenceFileDocument { static var readableContentTypes: [UTType] { [.replayable] } @Published var x = 0 init() {} init(configuration: ReadConfiguration) throws {} func snapshot(contentType: UTType) throws -> Data { Data() } func fileWrapper(snapshot: Data, configuration: WriteConfiguration) throws -> FileWrapper { .init(regularFileWithContents: snapshot) } } extension UTType { static var replayable: UTType { UTType(exportedAs: "com.whatever.yo") } }
2
0
78
Nov ’25
EditButton selection gets cleared when confirmationDialog appears in SwiftUI List
I'm experiencing an issue where my List selection (using EditButton) gets cleared when a confirmationDialog is presented, making it impossible to delete the selected items. Environment: Xcode 16.0.1 Swift 5 iOS 18 (targeting iOS 17+) Issue Description: When I select items in a List using EditButton and tap a delete button that shows a confirmationDialog, the selection is cleared as soon as the dialog appears. This prevents me from accessing the selected items to delete them. Code: State variables: @State var itemsSelection = Set<Item>() @State private var showDeleteConfirmation = false List with selection: List(currentItems, id: \.self, selection: $itemsSelection) { item in NavigationLink(value: item) { ItemListView(item: item) } } .navigationDestination(for: Item.self) { item in ItemViewDetail(item: item) } .toolbar { ToolbarItem(placement: .primaryAction) { EditButton() } } Delete button with confirmation: Button { if itemsSelection.count > 1 { showDeleteConfirmation = true } else { deleteItemsSelected() } } label: { Image(systemName: "trash.fill") .font(.system(size: 12)) .foregroundStyle(Color.red) } .padding(8) .confirmationDialog( "Delete?", isPresented: $showDeleteConfirmation, titleVisibility: .visible ) { Button("Delete", role: .destructive) { deleteItemsSelected() } Button("Cancel", role: .cancel) {} } message: { Text("Going to delete: \(itemsSelection.count) items?") } Expected Behavior: The selected items should remain selected when the confirmationDialog appears, allowing me to delete them after confirmation. Actual Behavior: As soon as showDeleteConfirmation becomes true and the dialog appears, itemsSelection becomes empty (count = 0), making it impossible to delete the selected items. What I've Tried: Moving the confirmationDialog to different view levels Checking if this is related to the NavigationLink interaction Has anyone encountered this issue? Is there a workaround to preserve the selection when showing a confirmation dialog?
1
0
125
Nov ’25
SwiftUI TextField height shrinks on first focus in iOS 26/Xcode 26
Hi Everyone, I’m encountering a layout issue in SwiftUI starting with iOS 26 / Xcode 26 (also reproduced on iPadOS 26). I would like to see whether anyone else has seen the same behaviour, and if there’s any known workaround or fix from Apple. Reproduction Minimal code: struct ContentView: View { @State private var inputText: String = "" var body: some View { TextField("", text: $inputText) .font(.system(size: 14, weight: .semibold)) .background(Capsule().foregroundStyle(.gray)) } } Environment: Xcode: 26.1 (17B55) Devices / Simulators: iOS 26.0, iOS 26.0.1, iOS 26.1 on iPhone 17 Pro Max; iPadOS 26 on iPad. OS: iOS 26 / iPadOS 26 Observed behaviour When the TextField is tapped for the first time (focus enters), its height suddenly decreases compared to the unfocused state. After this initial focus-tap, subsequent unfocus/focus cycles preserve the height (i.e., height remains “normal” and stable). Prior to iOS 26 (i.e., in older iOS versions) I did not observe this behaviour — same code worked fine. I have not explicitly set .frame(height:) or extra padding, and I’m relying on the default intrinsic height + the Capsule background. Work-arounds Adding .frame(height: X) (fixed height) removes the issue (height stable). Thanks in advance for any feedback!
0
1
99
Nov ’25
Black flash when deleting list row during zoom + fullScreenCover transition in Light mode
[Submitted as FB20978913] When using .navigationTransition(.zoom) with a fullScreenCover, deleting the source item from the destination view causes a brief black flash during the dismiss animation. This is only visible in Light mode. REPRO STEPS Build and run the sample code below. Set the device to Light mode. Tap any row to open its detail view. In the detail view, tap Delete. Watch the dismiss animation as the list updates. EXPECTED The zoom transition should return smoothly to the list with no dark or black flash. ACTUAL A visible black flash appears over the deleted row during the collapse animation. It starts black, shortens, and fades out in sync with the row-collapse motion. The flash lasts about five frames and is consistently visible in Light mode. NOTES Occurs only when deleting from the presented detail view. Does not occur when deleting directly from the list. Does not occur or is not visible in Dark mode. Reproducible on both simulator and device. Removing .navigationTransition(.zoom) or using .sheet instead of .fullScreenCover avoids the issue. SYSTEM INFO Version 26.1 (17B55) iOS 26.1 Devices: iPhone 17 Pro simulator, iPhone 13 Pro hardware Appearance: Light Reproducible 100% of the time SAMPLE CODE import SwiftUI struct ContentView: View { @State private var items = (0..<20).map { Item(id: $0, title: "Item \($0)") } @State private var selectedItem: Item? @Namespace private var ns var body: some View { NavigationStack { List { ForEach(items) { item in Button { selectedItem = item } label: { HStack { Text(item.title) Spacer() } .padding(.vertical, 8) .contentShape(Rectangle()) } .buttonStyle(.plain) .matchedTransitionSource(id: item.id, in: ns) .swipeActions { Button(role: .destructive) { delete(item) } label: { Label("Delete", systemImage: "trash") } } } } .listStyle(.plain) .navigationTitle("Row Delete Issue") .navigationSubtitle("In Light mode, tap item then tap Delete to see black flash") .fullScreenCover(item: $selectedItem) { item in DetailView(item: item, ns: ns) { delete(item) selectedItem = nil } } } } private func delete(_ item: Item) { withAnimation { items.removeAll { $0.id == item.id } } } } struct DetailView: View { let item: Item let ns: Namespace.ID let onDelete: () -> Void @Environment(\.dismiss) private var dismiss var body: some View { NavigationStack { VStack(spacing: 30) { Text(item.title) Button("Delete", role: .destructive, action: onDelete) } .navigationTitle("Detail") .toolbar { Button("Close") { dismiss() } } } .navigationTransition(.zoom(sourceID: item.id, in: ns)) } } struct Item: Identifiable, Hashable { let id: Int let title: String } SCREEN RECORDING
0
0
51
Nov ’25