Post

Replies

Boosts

Views

Activity

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
132
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
246
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
162
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
143
May ’25
How do you restore a Sheet's window frame in SwiftUI for macOS
On macOS, it's not uncommon to present windows as sheets that can be resized. By setting the NSWindow's various frame auto save properties, you can restore the size of the sheet the next time it is presented. When presenting a Sheet from within SwiftUI using the .sheet view modifier, how can I preserve and restore the sheet's frame size? The closest I've been able to come is to put the SwiftUI view into a custom NSHostingController and then into an NSViewControllerRepresentable and then override viewWillAppear and look for self.view.window, which is all little awkward. Is there a more idiomatic way to achieve this in "pure" SwiftUI?
2
0
124
May ’25
How to correctly set a Picker's selection and contents in SwiftUI for macOS?
How do you atomically set a Picker's selection and contents on macOS such that you don't end up in a situation where the selection is not present within the Picker's content? I presume Picker on macOS is implemented as an NSPopUpButton and an NSPopUpButton doesn't really like the concept of "no selection". SwiftUI, when presented with that, outputs: Picker: the selection "nil" is invalid and does not have an associated tag, this will give undefined results. Consider the following pseudo code: struct ParentView: View { @State private var items: [Item] var body: some View { ChildView(items: items) } } struct ChildView: View { let items: [Item] @State private var selectedItem: Item? var body: some View { Picker("", selection: $selectedItem) { ForEach(items) { item in Text(item.name).tag(item) } } } } When items gets passed down from ParentView to the ChildView, it's entirely possible that the current value in selectedItem represents an Item that is not longer in the items[] array. You can "catch" that by using .onAppear, .task, .onChange and maybe some other modifiers, but not until after at least one render pass has happened and an error has likely been reported because selectedItem is nil or it's not represented in the items[] array. Because selectedItem is private state, a value can't easily be passed down from the parent view, though even if it could that just kind of moves the problem one level higher up. What is the correct way to handle this type of data flow in SwiftUI for macOS?
1
0
205
May ’25
Is it reasonable to vend an NSView from a "ViewModel" when using NSViewRepresentable instead of implementing the Coordinator pattern?
I'm currently integrating SwiftUI into an AppKit based application and was curious if the design pattern below was viable or not. In order to "bridge" between AppKit and SwiftUI, most of my SwiftUI "root" views have aViewModel that is accessible to the SwiftUI view via @ObservedObject. When a SwiftUI views need to use NSViewRepresentable I'm finding the use of a ViewModel and a Coordinator to be an unnecessary layer of indirection. In cases where it makes sense, I've just used the ViewModel as the Coordinator and it all appears to be working ok, but I'm curious if this is reasonable design pattern or if I'm overlooking something. Consider the following pseudo code: // 1. A normal @ObservedObject acting as the ViewModel that also owns and manages an NSTableView. @MainActor final class ViewModel: ObservedObject, NSTableView... { let scrollView: NSScrollView let tableView: NSTableView @Published var selectedTitle: String init() { // ViewModel manages tableView as its dataSource and delegate. tableView.dataSource = self tableView.delegate = self } func reload() { tableView.reloadData() } // Update view model properties. // Simpler than passing back up through a Coordinator. func tableViewSelectionDidChange(_ notification: Notification) { selectedTitle = tableView.selectedItem.title } } // 2. A normal SwiftUI view, mostly driven by the ViewModel. struct ContentView: View { @ObservedObject model: ViewModel var body: some View { Text(model.selectedTitle) // No need to pass anything down other than the view model. MyTableView(model: model) Button("Reload") { model.reload() } Button("Delete") { model.deleteRow(...) } } } // 3. A barebones NSViewRepresentable that just vends the required NSView. No other state is required as the ViewModel handles all interactions with the view. struct MyTableView: NSViewRepresentable { // Can this even be an NSView? let model: ViewModel func makeNSView(context: Context) -> some NSView { return model.scrollView } func updateNSView(_ nsView: NSViewType, context: Context) { // Not needed, all updates are driven through the ViewModel. } } From what I can tell, the above is working as expected, but I'm curious if there are some situations where this could "break", particularly around the lifecycle of NSViewRepresentable Would love to know if overall pattern is "ok" from a SwiftUI perspective.
0
0
66
Apr ’25
The @Environment(\.dismiss) value in SwiftUI for macOS does not dismiss a sheet presented by an NSWindowController.
I'm wondering what the correct, or recommended, way is to dismiss a SwiftUI that is being presented as a sheet hosted by an NSHostingController. The usual technique of invoking @Environment(\.dismiss) does not appear to work. Consider the code below. An NSWindowController is attempting to display a SwiftUI SettingsView as a sheet. The sheet is correctly presented, but the SettingsView is unable to dismiss itself. I am able to make it work by passing a closure into SettingsView that calls back to the NSWindowController but it's rather convoluted because SettingsView doesn't know the view controller that's hosting it until after SettingsView has been created, which means "finding" that view controller in the window controller to dismiss is more involved than it should be. Is there a better strategy to leverage here? final class MyViewController: NSViewController { @IBAction func buttonClicked(_ sender: NSButton) { if let presenter = window?.contentViewController { presenter.presentAsSheet(NSHostingController(rootView: SettingsView())) } } } struct SettingsView: View { @Environment(\.dismiss) private var dismiss var body: some View { VStack { Button("Cancel", role: .cancel) { dismiss() // This call does not dismiss the sheet. } .keyboardShortcut(.cancelAction) } } } Thank you. macOS 15.4.1 (24E263), Xcode 16.3 (16E140)
0
0
94
Apr ’25
Does MKLookAroundViewController work on macOS?
Trying to incorporate a LookAroundView into my macOS application but unable to make the LookAroundView interactive at all. I can get it to display a static image, but there's no interactivity at all and no controls visible. This is using the SwiftUI LookAroundPreview view as well as trying to wrap MKLookAroundViewController inside an NSViewRepresentable. The navigation properties are set to true but that doesn't seem to make a difference. Would love to incorporate this feature but without interactivity its value is limited. macOS 15.4.1 (24E263), Xcode Version 16.3 (16E140)
1
0
116
Apr ’25
How to create a momentary segmented control in SwiftUI for macOS?
In AppKit, NSSegmentedControl has various styles defined by NSSegmentStyle and various tracking modes defined by NSSegmentSwitchTracking. How can we set these properties in SwiftUI? I'm currently using a Picker with the view modifier .pickerStyle(.segmented) applied but this seems to produce a segmented control with tracking set to "select one". In particular I'm looking for momentary tracking so that I can create navigation-style buttons for backward/forward navigation. Under AppKit, the canonical way to do this is an NSSegmentedControl of style separated and tracking momentary. Is that possible under SwiftUI for macOS? (Using the latest versions of everything.)
1
0
127
Apr ’25
Can SwiftUI on macOS create an NSComboButton?
Without resorting to NSViewRepresentable, is there a view or view modifier in SwiftUI that can create an NSComboButton on macOS? NSComboButton was introduced in macOS 13 and is (relatively) new to AppKit: Apple Developer - NSComboButton I only require support on macOS for this control. Note that this is not to be confused with NSComboBox, which is a completely different control.
1
0
131
Apr ’25
Should you access @State properties from an NSViewController (AppKit / SwiftUI Integration)?
I'm currently working on a project to integrate some SwiftUI components into an existing AppKit application. The application makes extensive use of NSViewControllers. I can easily bridge between AppKit and SwiftUI using a view model that conforms to ObservableObject and is shared between the NSViewController and the SwiftUI View. But it's kind of tedious creating a view model for every view. Is it "safe" and "acceptable" for the NSViewController to "hold on" to the SwiftUI View that it creates and then access its @State or @StateObject properties? The lifecycle of DetailsView, a SwiftUI View, isn't clear to me when viewed through the lens of an NSViewController. Consider the following: import AppKit import SwiftUI struct DetailsView: View { @State var details: String = "" var body: some View { Text(details) } } final class ViewController: NSViewController { private let detailsView: DetailsView init() { self.detailsView = DetailsView() super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { view.addSubview(NSHostingView(rootView: detailsView)) } func updateDetails(_ details: String) { // Is this 'safe' and 'acceptable'? self.detailsView.details = details } } Is the view controller guaranteed to always be updating the correct @State property or is there a chance that the view controller's reference to it somehow becomes stale because of a SwiftUI update?
2
0
137
Apr ’25
Re-Visiting NSViewController.loadView's behaviour in 2024 and under macOS 15...
This is a post down memory lane for you AppKit developers and Apple engineers... TL;DR: When did the default implementation of NSViewController.loadView start making an NSView when there's no matching nib file? (I'm sure that used to return nil at some point way back when...) If you override NSViewController.loadView and call [super loadView] to have that default NSView created, is it safe to then call self.view within loadView? I'm refactoring some old Objective-C code that makes extensive use of NSViewController without any use of nibs. It overrides loadView, instantiates all properties that are views, then assigns a view to the view controller's view property. This seems inline with the documentation and related commentary in the header. I also (vaguely) recall this being a necessary pattern when not using nibs: @interface MyViewController: NSViewController // No nibs // No nibName @end @implementation MyViewController - (void)loadView { NSView *hostView = [[NSView alloc] initWithFrame:NSZeroRect]; self.button = [NSButton alloc...]; self.slider = [NSSlider alloc...]; [hostView addSubview:self.button]; [hostView addSubview:self.slider]; self.view = hostView; } @end While refactoring, I was surprised to find that if you don't override loadView and do all of the setup in viewDidLoad instead, then self.view on a view controller is non-nil, even though there was no nib file that could have provided the view. Clearly NSViewController has realized that: There's no nib file that matches nibName. loadView is not overridden. Created an empty NSView and assigned it to self.view anyways. Has this always been the behaviour or did it change at some point? I could have sworn that if there as no matching nib file and you didn't override loadView, then self.view would be nil. I realize some of this behaviour changed in 10.10, as noted in the header, but there's no mention of a default NSView being created. Because there are some warnings in the header and documentation around being careful when overriding methods related to view loading, I'm curious if the following pattern is considered "safe" in macOS 15: - (void)loadView { // Have NSViewController create a default view. [super loadView]; self.button = [NSButton...]; self.slider = [NSSlider...]; // Is it safe to call self.view within this method? [self.view addSubview:self.button]; [self.view addSubview:self.slider]; } Finally, if I can rely on NSViewController always creating an NSView for me, even when a nib is not present, then is there any recommendation on whether one should continue using loadView or instead move code the above into viewDidLoad? - (void)viewDidLoad { self.button = [NSButton...]; self.slider = [NSSlider...]; // Since self.view always seems to be non-nil, then what // does loadView offer over just using viewDidLoad? [self.view addSubview:self.button]; [self.view addSubview:self.slider]; } This application will have macOS 15 as a minimum requirement.
0
1
487
Nov ’24
Possibly Incorrect Statement in AppKit Release Notes for macOS 14.
While trying to debug some weird drawing issues under macOS 14, I remembered that there was a comment in the AppKit Release notes related to drawing and NSView.clipsToBounds. AppKit Release Notes for macOS 14 Under the section titled NSView, the following statement is made: For applications linked against the macOS 14 SDK, the default value of this property is true. Apps linked against older SDKs default to false. Some classes, like NSClipView, continue to default to true. Is this statement possibly backwards? From what I can tell, under macOS 14 NSView.clipsToBounds now defaults to false. I came across this while trying to debug an issue where views that override drawRect with the intent of calling NSFillRect(self.bounds) with a solid color are, sometimes, briefly flickering because self.bounds is NSZeroRect, even though self.frame is not (nor is the dirtyRect). This seems to be happening when views are added as subviews to a parent view. The subviews, which override drawRect, periodically "miss" a repaint and thus flicker. This seems to happen when views are frequently added or removed, like what happens in a scrolling view that is "recycling" views as they go offscreen. Views that scroll into the viewport are added as subviews and, sometimes, briefly flicker. Replacing calls to drawRect with wantsUpdateLayer and updateLayer eliminates the flickering, which makes me think something is going astray in drawRect and the various rects you can use. This is with Xcode 15.4, linking against macOS 14.5 and running on macOS 14.6.1
2
0
729
Oct ’24