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

Implicit list row animations broken in Form container on iOS 26 beta 3
[Submitted as FB18870294, but posting here for visibility.] In iOS 26 beta 3 (23A5287g), implicit animations no longer work when conditionally showing or hiding rows in a Form. Rows with Text or other views inside a Section appear and disappear abruptly, even when wrapped in withAnimation or using .animation() modifiers. This is a regression from iOS 18.5, where the row item animates in and out correctly with the same code. Repro Steps Create a new iOS App › SwiftUI project. Replace its ContentView struct with the code below Build and run on an iOS 18 device. Tap the Show Middle Row toggle and note how the Middle Row animates. Build and run on an iOS 26 beta 3 device. Tap the Show Middle Row toggle. Expected Middle Row item should smoothly animate in and out as it does on iOS 18. Actual Middle Row item appears and disappears abruptly, without any animation. Code struct ContentView: View { @State private var showingMiddleRow = false var body: some View { Form { Section { Toggle( "Show **Middle Row**", isOn: $showingMiddleRow.animation() ) if showingMiddleRow { Text("Middle Row") } Text("Last Row") } } } }
1
3
128
5d
How to use protocols to support managing SwiftUI views from different modules ?
In out project, we are creating a modular architecture where each module conform to certain protocol requirements for displaying the UI. For example: public protocol ModuleProviding: Sendable { associatedtype Content: View /// Creates and returns the main entry point view for this module /// - Parameter completion: Optional closure to be called when the flow completes /// - Returns: The main view for this module @MainActor func createView(_ completion: (() -> Void)?) -> Content } This protocol can be implemented by all different modules in the app, and I use a ViewProvider structure to build the UI from the module provided: public struct ViewProvider: Equatable { let id = UUID() let provider: any ModuleProviding let completion: () -> Void public init(provider: any ModuleProviding, completion: @escaping () -> Void) { self.provider = provider self.completion = completion } @MainActor public func layoutUI() -> some View { provider.createView(completion) } This code throws an error: Type 'any View' cannot conform to 'View' To solve this error, there are two ways, one is to wrap it in AnyView, which I don't want to do. The other option is to type check the provider with its concrete type: @ViewBuilder @MainActor private func buildViewForProvider(_ provider: any ModuleProviding, completion: (() -> Void)?) -> some View { switch provider { case let p as LoginProvider: p.createView(completion) case let p as PostAuthViewProvider: p.createView(completion) case let p as OnboardingProvider: p.createView(completion) case let p as RewardsProvider: p.createView(completion) case let p as SplashScreenProvider: p.createView(completion) default: EmptyView() } } This approach worked, but it defeats the purpose of using protocols and it is not scalable anymore. Are there any other approaches I can look at ? Or is this limitation in SwiftUI ?
3
0
91
5d
Disable Hit Testing on SwiftUI Map Annotation Content
Hi, I’m using SwiftUI’s Map with custom Annotation content and trying to make the annotation view ignore touch interactions—similar to applying .allowsHitTesting(false) on regular views. The goal is to ensure that map gestures such as long press and drag are not blocked by annotation content. However, setting .allowsHitTesting(false) on the annotation content doesn’t seem to have any effect. Is there any workaround or supported approach to allow the map to receive gestures even when they originate from annotation views? Thanks in advance for any guidance!
0
0
56
5d
Keeping the glass-style background inside a sheet with presentationDetents when navigating inside the sheet
Is there a way to keep the glass-style background on iOS 26 for a sheet with .presentationDetents when you navigate into a NavigationStack inside the sheet? Root level with glass: After following a NavigationLink inside the stack, the background is always opaque: The UI I'm building is somewhat similar to the Maps app, which resorts to showing another sheet on top of the main sheet when you select a location on the Map). I'm trying to get a similar look but with a proper navigation hierarchy inside the sheet's stack. Example code: import SwiftUI import MapKit struct ContentView: View { var body: some View { Map() .sheet(isPresented: .constant(true)) { NavigationStack { VStack { Text("Hello") NavigationLink("Show Foo") { Text("Foo") .navigationTitle("Foo") .presentationBackground(Color.clear) } } } .presentationDetents([.medium, .large]) .presentationBackgroundInteraction(.enabled) } } } #Preview { ContentView() }
Topic: UI Frameworks SubTopic: SwiftUI
1
0
160
5d
Using NSHostingSceneRepresentation to open arbitrary window in AppKit app
I’m trying to open a window from a SwiftUI Scene inside an AppKit app using NSHostingSceneRepresentation (macOS 26). The idea is that I want to call openWindow(id: "some-id") to show the new window. However, nothing happens when I try this — no window appears, and there’s nothing in the logs. Interestingly, if I use the openSettings() route instead, it does open a window. Does anyone know the correct way to open an arbitrary SwiftUI window scene from an AppKit app? import Cocoa import SwiftUI @main class AppDelegate: NSObject, NSApplicationDelegate { let settingsScene = NSHostingSceneRepresentation { #if true MyWindowScene() #else Settings { Text("My Settings") } #endif } func applicationWillFinishLaunching(_ notification: Notification) { NSApplication.shared.addSceneRepresentation(settingsScene) } @IBAction func showAppSettings(_ sender: NSMenuItem) { #if true settingsScene.environment.openWindow(id: MyWindowScene.windowID) #else settingsScene.environment.openSettings() #endif } } struct MyWindowScene: Scene { static let windowID = "com.company.MySampleApp.myWindow" var body: some Scene { Window("Sample Window", id: MyWindowScene.windowID) { Text("Sample Content") .scenePadding() } } }
Topic: UI Frameworks SubTopic: SwiftUI
0
0
65
5d
iOS 26 ScrollView with static background image
I need a layout where I have a ScrollView with some content, and ScrollView has full screen background image. Screen is pushed as detail on stack. When my screen is pushed we display navigation bar. We want a new scrollEdgeEffectStyle .soft style work. But when we scroll the gradient blur effect bellow bars is fixed to top and bottom part of the scroll view background image and is not transparent. However when content underneath navigation bar is darker and navigation bar changes automatically to adapt content underneath the final effect looks as expected doesn't use background image. Expected bahaviour for us is that the effect under the navigation bar would not use background image but would be transparent based on content underneath. This is how it is intialy when user didn't interact with the screen: This is how it looks when user scrolls down: This is how it looks when navigation bar adapts to dark content underneath: Minimal code to reproduce this behaviour: import SwiftUI @main struct SwiftUIByExampleApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { var body: some View { NavigationStack { ScrollView(.vertical) { VStack(spacing: 0.0) { ForEach(1 ..< 101, id: \.self) { i in HStack { Text("Row \(i)") Spacer() } .frame(height: 50) .background(Color.random) } } } .scrollEdgeEffectStyle(.soft, for: .all) .scrollContentBackground(.hidden) .toolbar { ToolbarItem(placement: .title) { Label("My Awesome App", systemImage: "sparkles") .labelStyle(.titleAndIcon) } } .toolbarRole(.navigationStack) .background( ZStack { Color.white .ignoresSafeArea() Image(.sea) .resizable() .ignoresSafeArea() .scaledToFill() } ) } } } extension Color { static var random: Color { Color( red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1) ) } } We've also tried using ZStack instead of .background modifier but we observed the same results. We want to basically achieve the same effect as showcased here, but with the static background image: https://youtu.be/3MugGCtm26A?si=ALG29NqX1jAMacM5&t=634
0
0
106
5d
`ContextMenu` and `Menu` Item Layout: Icon/Title Order Discrepancy Between System and Custom Apps (iOS 26)
I've observed a difference in the layout of menu items within ContextMenu and Menu when comparing system applications to my own SwiftUI app, specifically concerning the order of icons and titles. On iOS 26, system apps (as shown in the image in the "System App" column) appear to display the item's icon before its title for certain menu items. However, in my SwiftUI app, when I use a Label (e.g. Label("Paste", systemImage: "doc.on.clipboard")) or an HStack containing an Image and Text, the icon consistently appears after the title within both ContextMenu and Menu items. I'm aiming to achieve the "icon first, then title" layout as seen in system apps. My attempts to arrange this order using HStack directly within the Button's label closure: Menu { Button { /* ... */ } label: { HStack { Image(systemName: "doc.on.clipboard") Text(String(localized: "Paste")) } } // ... } label: { Text("タップミー") } seem to be overridden or restricted by the OS, which forces the icon to the leading position (as shown in the image in the "Custom App" column). System App Custom App Is there a specific SwiftUI modifier, or any other setting I might have overlooked that allows developers to control the icon/title order within ContextMenu or Menu items to match the system's behavior? Or is this a system-controlled layout that app developers currently cannot customize, and we need to wait for potential changes from Apple to expose this capability for in-app menus? Thanks in advance!
1
0
130
5d
Buttons in the bottom bar of an inspector appear disabled
Buttons placed in the bottomBar and keyboard toolbar item positions in an inspector appear disabled/grayed out when at the large presentation detent. The same is not true for sheets. Is this intentional or a bug? If intentional, is there any backing design theory in the Human Interface Guidelines for it? Xcode 16.4 / 18.5 simulator // Inspector @ large detent struct ContentView: View { var body: some View { Color.clear .inspector(isPresented: .constant(true)) { Color.clear .presentationDetents([.large]) .toolbar { ToolbarItem(placement: .bottomBar) { Button("Save") {} .border(.red) } } } } } // Sheet struct ContentView: View { var body: some View { Color.clear .sheet(isPresented: .constant(true)) { Color.clear .presentationDetents([.medium]) .toolbar { ToolbarItem(placement: .bottomBar) { Button("Save") {} .border(.red) } } } } }
0
0
101
6d
How can I make an "inverted" list (bottom to top) with SwiftUI?
I'm trying to figure out how to make an inverted list in my watchOS app for a message view, so that messages appear from the bottom first, and go up. Everything I've tried so far has some sort of major drawback, and I'm wondering if there's some proper way to do it. My current implementation is flipping every message item upside-down, then flipping the whole list upside-down. This works in making the list go from bottom to top, but the digital crown scroll direction is also inverted. Simply inverting the array of messages doesn't work either, as the user has to scroll to the bottom of the list manually every time. Any tips/suggestions would be greatly appreciated.
1
0
431
6d
iPadOS: New system Menu hijacks "Open" command and I can not override it.
In my app, I had an '.commands' region that provided an "Open File" shortcut, bound to Command-o. My app manages multiple files, so this triggers my own file picker and opens the file. With iOS 2026, the system will not let me use this command, as the system is adding its own handler 'Open...' bound to the same shortcut, and with a target action called open:. My app also relies on LSSupportsOpeningDocumentsInPlace, which I believe is what triggers this behavior. Attempts to override it produce errors like this: Replacement elements conflict with existing elements: Keyboard Shortcut (duplicate modifierFlags: command, input: O): - Existing keyboard shortcut: <UIKeyCommand: 0x107cf4620> -> Title: Open... Action: open: Input: o + (UIKeyModifierCommand) - Conflicting keyboard shortcut: <UIKeyCommand: 0x107cf56c0> -> Title: Open Action: _performMainMenuShortcutKeyCommand: Input: o + (UIKeyModifierCommand) Ensure all keyboard shortcuts are unique to avoid undefined behavior. Mine is the open without the ellipsis. I would be happy if I could provide my own implementation of 'Open' and let SwiftUI call it, but I have not found any documentation for how to do this. I managed to override the buildMenu on the AppDelegate class, and while this removes the system version of 'open' SwiftUI is still going behind my back and disabling the menu entry (I suspect it is looking up the command by shortcut/modifier). So the closest I could do is to remove the system open menu with the buildMenu override, and then provide my own command with a different keyboard modifier (Control-o, instead of Command-o, which is ugly as hell, as everything else uses Command-Letter). Any guidance would be appreciated.
Topic: UI Frameworks SubTopic: SwiftUI
0
0
103
6d
CanvasView overlay on PDFKit loses quality when zoomed – how to preserve drawing resolution?
Hi all, I’m currently building a SwiftUI app that overlays a PKCanvasView onto each page of a PDFView using PDFPageOverlayViewProvider. It works well at the initial scale, but once I zoom into the PDF, the drawings on the PKCanvasView appear blurry or pixelated, even though the PDF itself remains crisp. I’m trying to adjust canvasView.contentScaleFactor relative to pdfView.scaleFactor to preserve the drawing quality. Here’s a simplified version of the relevant code: import SwiftUI import PDFKit import PencilKit struct ContentView: View { var body: some View { if let url = Bundle.main.url(forResource: "sample", withExtension: "pdf"), let data = try? Data(contentsOf: url), let document = PDFDocument(data: data) { PDFRepresentableView(document: document) } else { Text("") } } } #Preview { ContentView() } struct PDFRepresentableView: UIViewRepresentable { let document: PDFDocument let pdfView = PDFView() func makeUIView(context: Context) -> PDFView { pdfView.displayMode = .singlePageContinuous pdfView.usePageViewController(false) pdfView.displayDirection = .vertical pdfView.pageOverlayViewProvider = context.coordinator pdfView.document = document pdfView.autoScales = false pdfView.minScaleFactor = 0.7 pdfView.maxScaleFactor = 4 NotificationCenter.default.addObserver( context.coordinator, selector: #selector(context.coordinator.onPageZoomAndPan), name: .PDFViewScaleChanged, object: pdfView ) return pdfView } func updateUIView(_ uiView: PDFView, context: Context) { // Optional: update logic if needed } func makeCoordinator() -> CustomCoordinator { return CustomCoordinator(parent: self) } } class CustomCoordinator: NSObject, PDFPageOverlayViewProvider, PKCanvasViewDelegate { let parent: PDFRepresentableView init(parent: PDFRepresentableView) { self.parent = parent } func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? { let canvasView = PKCanvasView() let rect = page.bounds(for: .mediaBox) canvasView.drawingPolicy = .anyInput canvasView.tool = PKInkingTool(.pen, color: .black, width: 10) canvasView.translatesAutoresizingMaskIntoConstraints = true canvasView.backgroundColor = .red.withAlphaComponent(0.1) canvasView.frame = rect canvasView.isScrollEnabled = false for subView in view.documentView?.subviews ?? [] { subView.isUserInteractionEnabled = true } return canvasView } @objc func onPageZoomAndPan() { parent.pdfView.documentView?.subviews.forEach { subview in if subview.theClassName == "PDFPageView", let pageViewPrivate = subview.value(forKey: "_private") as? NSObject, let page = pageViewPrivate.value(forKey: "page") as? PDFPage { subview.subviews.forEach { subview in if let canvasView = subview as? PKCanvasView { let zoomScale = parent.pdfView.scaleFactor canvasView.contentScaleFactor = UIScreen.main.scale * zoomScale canvasView.drawing = canvasView.drawing canvasView.setNeedsDisplay() canvasView.layoutIfNeeded() } } } } print("Zoom changed. Current scale: \(parent.pdfView.scaleFactor)") } } extension NSObject { var theClassName: String { return NSStringFromClass(type(of: self)) } } But this doesn’t seem to improve the rendered quality. The lines still appear blurry when zoomed in. What I’ve tried: • Adjusting contentScaleFactor and forcing redraw • Reassigning canvasView.drawing • Calling setNeedsDisplay() and layoutIfNeeded() None of these approaches seem to re-render the canvas at a higher resolution or match the zoomed scale of the PDF. My questions: 1. Is there a correct way to scale PKCanvasView content to match PDF zoom levels? 2. Should I recreate the canvas or drawing when zoom changes? 3. Is PKCanvasView just not intended to handle high zoom fidelity? If anyone has successfully overlaid high-resolution canvas drawing on a zoomable PDFView, I’d love to hear how you managed it. Thanks in advance!
0
0
68
6d
Using NavigationSplitView with NavigationStack in it with variable navigation depth (2 and 3 levels) — unexpected back navigation on iPhone
I’m building a cross-platform app targeting macOS, iPad, and iPhone. My app currently uses both 2-level and 3-level navigation workflows: 3-level navigation: First level: Categories Second level: List of items in the selected category Third level: Detail view for a specific item 2-level navigation: First level: Category Second level: A singleton detail view (for example, StatusView). It does not have concept of List. After watching a couple of WWDC videos about multi-platform navigation, I decided to go with NavigationSplitView. However, on macOS, a 3-column NavigationSplitView felt a bit overwhelming to my eyes when the third column was empty—especially for the occasional 2-level navigation case. So I removed the third column and instead embedded a NavigationStack in the second column. According to the official Apple documentation, this is supported: You can also embed a NavigationStack in a column. The code with NavigationStack in NavigationSplitView works fine on macOS. But on iPhone, for the same code I’m seeing unexpected behavior: The first time I tap on the “Actions” category, it briefly shows the “Select an item” view, and then automatically pops back to the all-categories view. If I tap the same "Actions" category again, it shows the list of actions correctly, and everything works fine until I terminate and relaunch the app. Here is a minimal reproducible example: import SwiftUI struct StatusView: View { var body: some View { NavigationStack { List { Text("Heartbeat: OK") Text("Connected to backend: OK") } } } } struct ActionListView: View { var body: some View { NavigationStack { List { NavigationLink(value: "Action 1 value") { Text("Action 1 label") } NavigationLink(value: "Action 2 value") { Text("Action 2 label") } } } .navigationDestination(for: String.self) { action in Text(action) } } } struct ContentView: View { var body: some View { NavigationSplitView { List { NavigationLink(value: "Actions") { Text("Actions (3 level)") } NavigationLink(value: "Modes") { Text("Modes (3 level)") } NavigationLink(value: "State") { Text("Status (2 level)") } } .navigationDestination(for: String.self) { category in switch category { case "Actions": ActionListView() case "Modes": Text("Modes View") case "State": StatusView() default: Text("Unknown Category") } } } detail: { Text("Select an item") } } } Questions and considerations: How can I prevent this unexpected automatic pop back to the root view on iPhone the first time I select a category? Future-proofing for more than 3 level navigation: In the future, I may allow users to navigate beyond three levels (e.g., an item in one category can reference another item in a different category). Is it correct to assume that to support this with back navigation, I’d need to keep using NavigationStack inside NavigationSplitView? Is embedding NavigationStack in a 2 column NavigationSplitView the only practical approach to handle mixed 2 and 3 navigation depth if I don't want the third column to be ever empty? On macOS, NavigationStack alone doesn’t feel appropriate for sidebar-based navigation. Does it mean everyone on macOS pretty much always use NavigationSplitView? Any advice or examples would be appreciated. Thanks!
Topic: UI Frameworks SubTopic: SwiftUI
0
0
79
1w
Using SwiftUI .sheet: ScrollView rendering issue when used inside NavigationStack
I am encounter an issue with the height of a ScrollView not rendering properly during the transition of a sheet from closed to open. This results in a gap between the bottom edge of the ScrollView and the bottom edge of the sheet during the animation. I am getting this issue when trying to use the ScrollView inside a NavigationStack and when using a PresentationDetent other than .large. The code snippet below, for example, suffers from the issue. ScrollView { Button("Reveal sheet") { isPresented = true } } .frame(maxWidth: .infinity) .background(.yellow) .sheet(isPresented: $isPresented) { VStack { NavigationStack { ScrollView { ForEach(0..<100, id: \.self) { number in Text("\(number)") } .frame(maxWidth: .infinity) } .background(.green) .presentationDetents([.medium]) } } } Here is what the issue looks like for this example. The issue occurs in: Simulator iPhone 16 iOS 18.4 Personal device (iPhone 16 iOS 18.4) Canvas preview
1
0
106
1w
SwiftUI & Xcode 26.0 beta3: Using Map in LazyVGrid causes "Observation tracking feedback loop"
The following code causes an error in Xcode 26.0 beta3 in iOS Simulator with iOS 26.0. After starting the App, scroll to the end of the grid and the app will break. Testet with Xcode Playground with platforms: [ .iOS("17.0")], Simplest possible variant: import SwiftUI import MapKit struct ContentView: View { var body: some View { NavigationStack { ScrollView { LazyVGrid(columns: [GridItem()]) { ForEach(1...12, id:\.self) { i in Text("Hello \(i)") .frame(height: 150) } Map { } .frame(height: 150) } } } } } Thrown error: Observation tracking feedback loop detected! Make a symbolic breakpoint at UIObservationTrackingFeedbackLoopDetected to catch this in the debugger. Refer to the console logs for details about recent invalidations; you can also make a symbolic breakpoint at UIObservationTrackingInvalidated to catch invalidations in the debugger. Object receiving repeated [updateProperties] invalidations: <UIKit.NavigationBarContentView: 0x103026000; frame = (0 0; 402 54); gestureRecognizers = <NSArray: 0x600000c26790>; layer = <CALayer: 0x600000c2d4a0>> contentView=0x0000000103026000 These three nested views are necessary to reproduce the error: NavigationStack -> ScrollView -> LazyVGrid In iOS Simulator with iOS 18.0 there is no error message, the CPU raises to 100%. With Xcode 16.4 the program runs error-free. Is there a solution?
2
1
135
1w
iOS 26: .tabViewBottomAccessory - How to open a new view on the tabViewBottomAccessory
If you are currently on the beta of iOS 26, open Apple Music and you'll see a tabViewBottomAccessory that is the mini NowPlayingView. When tapped, it opens the NowPlayingView. Is there a similar way to do this in SwiftUI? Looking through Apple's documentation, they do not specify any way to reproduce the same kind of view transition. This is the Apple Music app with the tabViewBottomAccessory. When clicked it opens the NowPlayingView
0
0
57
1w
FileExporter's action label always says "Move"
I would like to implement an "Export" dialog using the .fileExporter() view modifier. The following code works correctly, but the FileExporter's action label always says "Move", which is inappropriate for the context. I would like to change it to say "Export", "Save", "Save as", or anything semantically correct. Button("Browse") { showingExporter = true } .buttonStyle(.borderedProminent) .fileExporter( isPresented: $showingExporter, document: document, contentType: .data, defaultFilename: suggestedFilename ?? fileUrl.pathComponents.last ?? "Untitled" ) { result in switch result { case .success(let url): print("Saved to \(url)") onSuccess() case .failure(let error): print(error.localizedDescription) onError(error) } } "document" is a custom FileDocument with a fileWrapper() method implemented like this: func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { return FileWrapper(regularFileWithContents: data) } This was tested on iOS 26 beta 3.
Topic: UI Frameworks SubTopic: SwiftUI
0
0
70
1w