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

"Unwrapping" Scrolling
For a number of complex views in my app, I need to embed scrollable objects (like List, Form or TextEditor) in a ScrollView. To do that, I need to apply a limit to the height of the embedded object. What I would like to do is set that limit to the actual height of the content being displayed in the List, Form, TextEditor, etc. (Note that this height calculation should be dynamic, so that content changes are properly displayed. I attempted the following: @State listHeight: CGFloat = .zero List { ... content here } .onScrollGeometryChange(for: CGFloat.self, of: { geometry in return geometry.contentSize.height }, action: { oldValue, newValue in if oldValue != newValue { listHeight = newValue } }) .frame(height: listHeight) .scrollDisabled(true) This does not work because geometry.contentSize.height is always 0. So it is apparent that .onScrollGeometryChangedoes not interact with the internal scrolling mechanism of List. (Note, however, that.scrollDisabled` does work.) Does anyone have a suggestion on how I might get this to work?
3
0
110
2w
How do you autoscroll a ScrollView during a DragGesture on macOS?
In SwiftUI for macOS, when implementing a DragGesture inside a ScrollVIew, how can I implement auto-scrolling when the mouse is not actively moving? In AppKit, this would normally be done with a periodic event so that auto-scrolling continues to take place even if the user isn't actively moving the mouse. This is essential behaviour when implementing something like a drag-to-select gesture. NSView.autoscroll(with: NSEvent) -> Bool Is there anything in SwiftUI or ScrollView to accomplish this behaviour?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
0
0
123
2w
On macOS, what is the appropriate way to disable the sidebar material in a NavigationSplitView?
If you create a NavigationSplitView, then the sidebar is automatically adorned with a sidebar material effect. This affects the views background as well as any controls that are in the view. What is the correct way to disable this behaviour so that I can use a NavigationSplitView without the material effects being applied? The best I've come up with so far is to explicitly set the background on the sidebar but I'm curious if that's the correct way or I'm just getting lucky. struct ContentView: View { var body: some View { NavigationSplitView { // This works, but is it correct? SidebarView() .background(.windowBackground) } detail: { DetailView() } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
4
0
120
2w
How do you observe the count of records in a Swift Data relationship?
What is the correct way to track the number of items in a relationship using SwiftData and SwiftUI? Imagine a macOS application with a sidebar that lists Folders and Tags. An Item can belong to a Folder and have many Tags. In the sidebar, I want to show the name of the Folder or Tag along with the number of Items in it. I feel like I'm missing something obvious within SwiftData to wire this up such that my SwiftUI views correctly updated whenever the underlying modelContext is updated. // The basic schema @Model final class Item { var name = "Untitled Item" var folder: Folder? = nil var tags: [Tag] = [] } @Model final class Folder { var name = "Untitled Folder" var items: [Item] = [] } @Model final class Tag { var name = "Untitled Tag" var items: [Item] = [] } // A SwiftUI view to show a Folder. struct FolderRowView: View { let folder: Folder // Should I use an @Query here?? // @Query var items: [Item] var body: some View { HStack { Text(folder.name) Spacer() Text(folder.items.count.formatted()) } } } The above code works, once, but if I then add a new Item to that Folder, then this SwiftUI view does not update. I can make it work if I use an @Query with an #Predicate but even then I'm not quite sure how the #Predicate is supposed to be written. (And it seems excessive to have an @Query on every single row, given how many there could be.) struct FolderView: View { @Query private var items: [Item] private var folder: Folder init(folder: Folder) { self.folder = folder // I've read online that this needs to be captured outside the Predicate? let identifier = folder.persistentModelID _items = Query(filter: #Predicate { link in // Is this syntax correct? The results seem inconsistent in my app... if let folder = link.folder { return folder.persistentModelID == identifier } else { return false } }) } var body: some View { HStack { Text(folder.name) Spacer() // This mostly works. Text(links.count.formatted()) } } } As I try to integrate SwiftData and SwiftUI into a traditional macOS app with a sidebar, content view and inspector I'm finding it challenging to understand how to wire everything up. In this particular example, tracking the count, is there a "correct" way to handle this?
1
0
111
2w
How do you compute backing pixel alignment in SwiftUI's `Layout`?
When performing custom layout in AppKit, it's essential that you pixel align frames using methods like backingAlignedRect. The alignment differs depending on the backingScaleFactor of the parent window. When building custom Layouts in SwiftUI, how should you compute the alignment of a subview.frame in placeSubviews() before calling subview.place(...)? Surprisingly, I haven't seen any mention of this in the WWDC videos. However, if I create a Rectangle of width 1px and then position it on fractional coordinates, I get a blurry view, as I would expect. Rounding to whole numbers works, but on Retina screens you should be able to round to 0.5 as well. func placeSubviews( in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Void ) { // This should be backing aligned based on the parent window's backing scale factor. var frame = CGRect( x: 10.3, y: 10.8, width: 300.6, height: 300.1 ) subview.place( at: frame.origin, anchor: .topLeading, proposal: ProposedViewSize(frame.size) ) }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
1
0
50
2w
Seeing some behaviour in Swift that I don't understand...
It's related to the passByValue nature of structs. In the sample code below, I'm displaying a list of structs (and I can add instances to my list using Int.random(1..<3) to pick one of two possible predefined versions of the struct). I also have a detail view that can modify the details of a single struct. However when I run this code, it will instead modify all the instances (ie either Sunday or Monday) in my list. To see this behaviour, run the following code and: tap New Trigger enough times that there are multiple of at least one of the sunday/monday triggers tap one of the matching trigger rows modify either the day, or the int expected: only one of the rows will reflect the edit actual: all the matching instances will be updated. This suggests to me that my Sunday and Monday static instances are being passed by reference when they get added to the array. But I had thought structs were strictly pass by value. What am I missing? thanks in advance for any wisdom, Mike struct ContentView: View { @State var fetchTriggers: [FetchTrigger] = [] var body: some View { NavigationView { VStack { Button("New Trigger") { fetchTriggers.append(Int.random(in: 1..<3) == 1 ? .sunMorning : .monEvening) } List($fetchTriggers) { fetchTrigger in NavigationLink(destination: FetchTriggerDetailView(fetchTrigger: fetchTrigger) .navigationBarTitle("Back", displayMode: .inline)) { Text(fetchTrigger.wrappedValue.description) .padding() } } } } } } struct FetchTrigger: Identifiable { static let monEvening: FetchTrigger = .init(dayOfWeek: .monday, hour: 6) static let sunMorning: FetchTrigger = .init(dayOfWeek: .sunday, hour: 3) let id = UUID() enum DayOfWeek: Int, Codable, CaseIterable, Identifiable { var id: Int { self.rawValue } case sunday = 1 case monday case tuesday var description: String { switch self { case .sunday: return "Sunday" case .monday: return "Monday" case .tuesday: return "Tuesday" } } } var dayOfWeek: DayOfWeek var hour: Int var description: String { "\(dayOfWeek.description), \(hour):00" } } struct FetchTriggerDetailView: View { @Binding var fetchTrigger: FetchTrigger var body: some View { HStack { Picker("", selection: $fetchTrigger.dayOfWeek) { ForEach(FetchTrigger.DayOfWeek.allCases) { dayOfWeek in Text(dayOfWeek.description) .tag(dayOfWeek) } } Picker("", selection: $fetchTrigger.hour) { ForEach(1...12, id: \.self) { number in Text("\(number)") .tag(number) } } } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
1
0
181
2w
VisionOS: WKWebView stops rendering after WindowGroup is closed
I'm building a visionOS app where users can place canvases into the 3D environment. These canvases are RealityKit entities that render web content using a WKWebView. The web view is regularly snapshotted, and its image is applied as a texture on the canvas surface. When the user taps on a canvas, a WindowGroup is opened that displays the same shared WKWebView instance. This works great: both the canvas in 3D and the WindowGroup reflect the same live web content. The canvas owns the WKWebView and keeps a strong reference to it. Problem: Once the user closes the WindowGroup, the 3D canvas stops receiving snapshot updates. The snapshot loop task is still running (verified via logs), but WKWebView.takeSnapshot(...) never returns — the continuation hangs forever. No error is thrown, no image is returned. If the user taps the canvas again (reopening the window), snapshots resume and everything works again. @MainActor private func getSnapshot() async -> UIImage? { guard !webView.isLoading else { return nil } let config = WKSnapshotConfiguration() config.rect = webView.bounds config.afterScreenUpdates = true return await withCheckedContinuation { continuation in webView.takeSnapshot(with: config) { image, _ in continuation.resume(returning: image) } } } What I’ve already verified: The WKWebView is still in memory. The snapshot loop (Task) is still running; it just gets stuck inside takeSnapshot(...). I tried keeping the WKWebView inside a hidden UIWindow (with .alpha = 0.01, .windowLevel = .alert + 1, and isHidden = false). Suspicion It seems that once a WKWebView is passed into SwiftUI and rendered (e.g., via UIViewRepresentable), SwiftUI takes full control over its lifecycle. SwiftUI tells WebKit "This view got closed" and WebKit reacts by stopping the WKWebView. Even though it still exists in memory and is being hold by a strong reference to it in the Canvas-Entity. Right now, this feels like a one-way path: Starting from the canvas, tapping it to open the WindowGroup works fine. But once the WindowGroup is closed, the WebView freezes, and snapshots stop until the view is reopened. Questions Is there any way under visionOS to: Keep a WKWebView rendering after its SwiftUI view (WindowGroup) is dismissed? Prevent WebKit from suspending or freezing the view? Embed the view in a persistent system-rendered context to keep snapshotting functional? For context, here's the relevant SwiftUI setup struct MyWebView: View { var body: some View { if let webView = WebViewManager.shared.getWebView() { WebViewContainer(webView: webView) } } } struct WebViewContainer: UIViewRepresentable { let webView: WKWebView func makeUIView(context: Context) -> UIView { return webView } func updateUIView(_ uiView: UIView, context: Context) {} }
1
0
134
2w
In SwiftUI for macOS, how can you detect if a view or any ancestor is "hidden"?
Given a View in SwiftUI for macOS, how can I tell if that view is hidden either because it, or any of its ancestor's opacity is 0.0 or the .hidden modifier has been applied? Presumably I can manually do this with an Environment value on the ancestor view, but I'm curious if this can be done more idiomatically. An example use case: I have views that run long-running Tasks via the .task(id:) modifier. These tasks only need to be running if the View itself is visible to the user. When the View is hidden, the task should stop. When the View reappears, the Task should restart. This happens automatically when Views are created and destroyed, but does not happen when a view is only hidden.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
2
0
80
2w
TabView with NavigationStack issue on macOS Tahoe Beta 7
I have an app using TabView with multiple Tabs using tabViewStyle „sidebarAdaptable“. Each Tab does have its own NavigationStack. When I switch multiple times between the tabs and append a child view to one of my navigation stacks, it will stop working after a few tries with the following error „A NavigationLink is presenting a value of type “HomeStack” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated. Note: Links search for destinations in any surrounding NavigationStack, then within the same column of a NavigationSplitView.“ The same code is working fine on iOS, iPadOS but not working on macOS. When I remove tabViewStyle of sidebarAdaptable it’s also working on macOS. I shrinked it down to a minimal reproducible code sample. Any idea if that is a bug or I'm doing something wrong? struct ContentView: View { @State private var appState: AppState = .init() var body: some View { TabView(selection: $appState.rootTab) { Tab("Home", systemImage: "house", value: RootTab.home) { NavigationStack(path: $appState.homeStack) { Text("Home Stack") NavigationLink("Item 1", value: HomeStack.item1) .padding() NavigationLink("Item 2", value: HomeStack.item2) .padding() .navigationDestination(for: HomeStack.self) { stack in Text("Stack \(stack.rawValue)") } .navigationTitle("HomeStack") } } Tab("Tab1", systemImage: "gear", value: RootTab.tab1) { NavigationStack(path: $appState.tab1Stack) { Text("Tab 1 Stack") NavigationLink("Item 1", value: Tab1Stack.item1) .padding() NavigationLink("Item 2", value: Tab1Stack.item2) .padding() .navigationDestination(for: Tab1Stack.self) { stack in Text("Stack \(stack.rawValue)") } .navigationTitle("Tab1Stack") } } } .tabViewStyle(.sidebarAdaptable) .onChange(of: appState.rootTab) { _, _ in appState.homeStack.removeAll() appState.tab1Stack.removeAll() } } } @MainActor @Observable class AppState { var rootTab: RootTab = .home var homeStack: [HomeStack] = [] var tab1Stack: [Tab1Stack] = [] } enum RootTab: Hashable { case home case tab1 case tab2 } enum HomeStack: String, Hashable { case home case item1 case item2 } enum Tab1Stack: String, Hashable { case home case item1 case item2 }
0
0
65
2w
Menu view flashes white before closing when device is set to dark appearance
Feedback ID: FB19846667 When dismissing a Menu view when the device is set to dark appearance, there is a flash of lightness that is distracting and feels unnatural. This becomes an issue for apps that rely on the user interacting with Menu views often. When using the overflow menu on a toolbar, the effect of dismissing the menu is a lot more natural and there is less flashing. I expect a similar visual effect when creating Menu views outside of a toolbar. Has anyone found a way around this somehow? Comparison between dismissing a menu and a toolbar overflow: https://www.youtube.com/shorts/H2gUQOwos3Y Slowed down version of dismissing a menu with a visible light flash: https://www.youtube.com/shorts/MBCCkK-GfqY
0
0
130
2w
iOS 26 @FocusState Doesn't Work If TextField Is In Toolbar
When I add a TextField with @FocusState to a toolbar, I noticed that setting focus = false doesn't cause the form to lose focus If I move the TextField out of the toolbar setting focus = false works fine. How can I unfocus the text field when the cancel button is tapped? Minimal example tested on Xcode Version 26.0 beta 6 (17A5305f): import SwiftUI struct ContentView: View { @State private var text: String = "" @FocusState private var focus: Bool var body: some View { NavigationStack { List { Text("Test List") } .toolbar { ToolbarItem(placement: .bottomBar) { TextField("Test", text: $text) .padding(.horizontal) .focused($focus) } ToolbarItem(placement: .bottomBar) { Button(role: .cancel) { focus = false // THIS DOESN'T WORK! } } } } } } #Preview { ContentView() }```
0
1
181
2w
.textContentType(.emailAddress) does not work as expected
The following code: struct ContentView: View { @State private var email = "" var body: some View { VStack { TextField("email", text: $email) .textContentType(.emailAddress) } } } does not work as expected. What I expected is to run the app and have it suggest my actual email when I click into the field. Instead what it does is display "Hide My Email" twice. Selecting the first "Hide My Email" pastes the actual text "Hide My Email" into the field, the second one brings up an iCloud sheet to select a random email. Trying some suggestions online for .emailAddress in general not working does not change anything: TextField("Email", text: $email) .textFieldStyle(RoundedBorderTextFieldStyle()) .textContentType(.emailAddress) .keyboardType(.emailAddress) .autocorrectionDisabled() .disableAutocorrection(true) .textInputAutocapitalization(.never)
Topic: UI Frameworks SubTopic: SwiftUI
1
0
111
2w
`Invalid frame dimension (negative or non-finite)` on iPhone 12 mini
Hi everyone, I’m encountering the following error when displaying a TextField inside a Form together with a ToolbarItem(placement: .keyboard): Invalid frame dimension (negative or non-finite). Environment This issue reproduces on iPhone 12 mini (iOS 18.6). The same code does not reproduce on iPhone 15. I confirmed that explicitly constraining the size of the TextField or Text with .frame(width:height:) does not resolve the issue. Minimal Reproducible Example import SwiftUI struct Test: View { @State var value: Int = 0 var body: some View { Form { TextField("0", value: $value, format: .number) } .toolbar { ToolbarItem(placement: .keyboard) { Text("Close") } } } } Has anyone else encountered this issue? Is this a known bug, or is there a recommended workaround for devices with smaller screen widths such as the iPhone 12 mini? Thanks in advance for your help! Best regards, Naoya Ozawa
Topic: UI Frameworks SubTopic: SwiftUI
0
0
50
2w