Post

Replies

Boosts

Views

Activity

Clarification on SwiftUI Environment Write Performance
I'm looking for clarification on a SwiftUI performance point mentioned in the recent Optimize your app's speed and efficiency | Meet with Apple video. (YouTube link not allowed, but the video is available on the Apple Developer channel.) At the 1:48:50 mark, the presenter says: Writing a value to the Environment doesn't only affect the views that read the key you're updating. It updates any view that reads from any Environment key. [abbreviated quote] That statement seems like a big deal if your app relies heavily on Environment values. Context I'm building a macOS application with a traditional three-panel layout. At any given time, there are many views on screen, plus others that exist in the hierarchy but are currently hidden (for example, views inside tab views or collapsed splitters). Nearly every major view reads something from the environment—often an @Observable object that acts as a service or provider. However, there are a few relatively small values that are written to the environment frequently, such as: The selected tab index The currently selected object on a canvas The Question Based on the presenter's statement, I’m wondering: Does writing any value to the environment really cause all views in the entire SwiftUI view hierarchy that read any environment key to have their body re-evaluated? Do environment writes only affect child views, or do they propagate through the entire SwiftUI hierarchy? Example: View A └─ View B ├─ View C └─ View D If View B updates an environment value, does that affect only C and D, or does it also trigger updates in A and B (assuming each view has at least one @Environment property)? Possible Alternative If all views are indeed invalidated by environment writes, would it be more efficient to “wrap” frequently-changing values inside an @Observable object instead of updating the environment directly? // Pseudocode @Observable final class SelectedTab { var index: Int } ContentView() .environment(\.selectedTab, selectedTab) struct TabView: View { @Environment(\.selectedTab) private var selectedTab var body: some View { Button("Action") { // Would this avoid invalidating all views using the environment? selectedTab.index = 1 } } } Summary From what I understand, it sounds like the environment should primarily be used for stable, long-lived objects—not for rapidly changing values—since writes might cause far more view invalidations than most developers realize. Is that an accurate interpretation? Follow-Up In Xcode 26 / Instruments, is there a way to monitor writes to @Environment?
4
0
1.1k
Nov ’25
NSOutlineView incorrectly draws disclosure indicator when item views are SwiftUI views.
I am using an NSOutlineView via NSViewRepresentable in a SwiftUI application running on macOS. Everything has been working fine. Up until lately, I've been returning a custom NSView for each item using the standard: func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { // View recycling omitted. return MyItemView(item) } Now I want to explore using a little bit more SwiftUI and returning an NSHostingView from this delegate method. func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { // View recycling omitted. let rootView = MySwiftUIView(item) let hostingView = NSHostingView(rootView: rootView) return hostingView } For the most part, this appears to be working fine. NSOutlineView is even correctly applying highlight styling, so that's great. But there's one small glitch. The outline view's disclosure triangles do not align with the hosting view's content. The disclosure triangles appear to just be pinned to the top. Perhaps they can't find a baseline constraint or something? Is there any SwiftUI modifier or AppKit/SwiftUI technique I can apply here to get the disclosure button to appear in the right place? Here is what the SwiftUI + NSHostingView version looks like: Note the offset disclosure indicators. (Image spacing is a bit off as well using Label, but fixable. Here is what an NSView with NSTextFields looks like: Disclosure indicators are correctly aligned, as you would expect.
1
0
127
Nov ’25
How to apply SwiftUI window modifiers when using Xcode's #Preview on a macOS view?
Is there a way to configure the style and toolbar of the macOS window that Xcode uses in #Preview? I am working on a macOS application and want to preview some SwiftUI views within different window styles, toolbar styles and window title/subtitle visibilities. Some of the modifiers to control the look-and-feel of a window are actually Scene Modifiers, not View Modifiers: .windowStyle .windowToolbarLabelStyle .windowToolbarStyle But #Preview does not accept Scenes, so I can't apply these modifiers: // Error, not a view modifier. #Preview { ContentView() .windowStyle(...) } // Error, Window is not supported in #Preview. #Preview { Window("Browser", id: "browser") { ContentView() } } If I give my ContentView a .toolbar(...), Xcode's Preview will correctly show a window with a toolbar, but not necessarily in the style I want. Is there a way to apply the Scene Modifiers to #Preview so that I can see how they affect the window's chrome and toolbar?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
0
0
66
Oct ’25
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
149
Aug ’25
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
172
Aug ’25
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
146
Aug ’25
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
77
Aug ’25
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
103
Aug ’25
How can I connect NSTableCellView.textField to a SwiftUI view?
When using NSTableView or NSOutlineView, if you use an NSTableCellView and wire up the .imageView and .textField properties then you get some "free" behaviour with respect to styling and sizing of those fields. (ex: They reflect the user's preferred "Sidebar Icon Size" as selected in Settings. ) If I'm using a SwiftUI View inside an NSTableCellView, is there any way to connect a Text or Image to those properties? Consider the following pseudo code: struct MyCellView: View { let text: String let url: URL? var body: some View { HStack { Image(...) // How to indicate this is .imageView? Text(...) // How to indicate this is .textField? } } } final class MyTableCellView: NSTableCellView { private var hostingView: NSHostingView<MyCellView>! init() { self.hostingView = NSHostingView(rootView: MyCellView(text: "", url: nil)) self.addSubview(self.hostingView) } func configureWith(text: String, url: URL) { let rootView = MyCellView(text: text, url: url) hostingView.rootView = rootView // How can I make this connection? self.textField = rootView.??? self.imageView = rootView.??? } } I'm ideally looking for a solution that works on macOS 15+.
2
0
115
Aug ’25
On macOS, how do you place a toolbar item on the trailing edge of the window's toolbar when an Inspector view is open?
Using SwiftUI on macOS, how can I add a toolbar item on the right-most (trailing) edge of the window's toolbar when an Inspector is used? At the moment, the toolbar items are all left-of (leading) the split view tracking separator. I want the inspector toolbar item to be placed similar to where Xcode's Inspector toolbar item is placed: always as far right (trailing) as possible. NavigationSplitView { // ... snip } detail: { // ... snip } .inspector(isPresented: $isInspectorPresented) { InspectorContentView() } .toolbar { // What is the correct placement value here? ToolbarItem(placement: .primaryAction) { Button { isInspectorPresented.toggle() } label: { Label("Toggle Inspector", systemImage: "sidebar.trailing") } } } See the attached screenshot. When the InspectorView is toggled open, the toolbar item tracks leading the split view tracking separator, which is not consistent with how Xcode works.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
4
0
107
Aug ’25
How to hide the tab bar in SwiftUI's TabView for macOS?
In SwiftUI for macOS, how can I hide the tab bar when using TabView? I would like to provide my own tab bar implementation. In AppKit's NSTabViewController, we can do the following: let tabViewController = NSTabViewController() tabViewController.tabStyle = .unspecified I've come across various posts that suggest using the .toolbar modifier, but none appear to work on macOS (or at least I haven't found the right implementation). struct ContentView: View { var body: some View { TabView { // ... content } <- which view modifier hides the tab bar? } } Latest macOS, Latest Xcode
3
0
208
May ’25
How to effectively use task(id:) when multiple properties are involved?
While adopting SwiftUI (and Swift Concurrency) into a macOS/AppKit application, I'm making extensive use of the .task(id:) view modifier. In general, this is working better than expected however I'm curious if there are design patterns I can better leverage when the number of properties that need to be "monitored" grows. Consider the following pseudo-view whereby I want to call updateFilters whenever one of three separate strings is changed. struct FiltersView: View { @State var argument1: String @State var argument2: String @State var argument3: String var body: some View { TextField($argument1) TextField($argument2) TextField($argument3) }.task(id: argument1) { await updateFilters() }.task(id: argument2) { await updateFilters() }.task(id: argument3) { await updateFilters() } } Is there a better way to handle this? The best I've come up with is to nest the properties inside struct. While that works, I now find myself creating these "dummy types" in a bunch of views whenever two or more properties need to trigger an update. ex: struct FiltersView: View { struct Components: Equatable { var argument1: String var argument2: String var argument3: String } @State var components: Components var body: some View { // TextField's with bindings to $components... }.task(id: components) { await updateFilters() } } Curious if there are any cleaner ways to accomplish this because this gets a bit annoying over a lot of views and gets cumbersome when some values are passed down to child views. It also adds an entire layer of indirection who's only purpose is to trigger task(id:).
2
0
125
May ’25
Can SwiftUI TextFields in a List on macOS be marked as always editable?
In SwiftUI's List, on macOS, if I embed a TextField then the text field is presented as non-editable. If the user clicks on the text and waits a short period of time, the text field will become editable. I'm aware this is generally the correct behaviour for macOS. However, is there a way in SwiftUI to supress this behaviour such that the TextField is always presented as being editable? I want a scrollable, List of editable text fields, much like how a Form is presented. The reason I'm not using a Form is because I want List's support for reordering by drag-and-drop (.onMove). Use Case A view that allows a user to compose a questionnaire. They are able to add and remove questions (rows) and each question is editable. They require drag-and-drop support so that they can reorder the questions.
0
0
118
May ’25