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
AppKit
RSS for tagConstruct and manage a graphical, event-driven user interface for your macOS app using AppKit.
Posts under AppKit tag
193 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I have an outside Mac App Store app. It has an action extension. I can't get it to run from Xcode. I try to debug it from Safari. It shows up in the menu when I click the 'rollover' button but it doesn't show up in the UI at all. Xcode doesn't give me any indication as to what the problem is. I see this logs out in console when I try to open the action extension:
Prompting policy for hardened runtime; service: kTCCServiceAppleEvents requires entitlement com.apple.security.automation.apple-events but it is missing for accessing={TCCDProcess: identifier=BundleIdForActionExtHere, pid=6650, auid=501, euid=501, binary_path=/Applications/AppNamehere.app/Contents/PlugIns/ActionExtension.appex/Contents/MacOS/ActionExtension}, requesting={TCCDProcess: identifier=com.apple.appleeventsd, pid=550, auid=55, euid=55, binary_path=/System/Library/CoreServices/appleeventsd},
I don't see why the Action extension needs Apple events but I added it to the entitlements anyway but it doesn't seem to matter. The action extension fails to open.
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:).
Crash Stack:
thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x19ba3bb04)
frame #0: 0x000000019ba3bb04 CoreFoundation`forwarding.cold.2 + 92
frame #1: 0x000000019b8ab718 CoreFoundation`forwarding + 1288
frame #2: 0x000000019b8ab150 CoreFoundation`_CF_forwarding_prep_0 + 96
frame #3: 0x000000019df230b0 CoreText`TCFRef<CTRun*>::Retain(void const*) + 40
frame #4: 0x000000019e052050 CoreText`CreateFontWithFontURL(__CFURL const*, __CFString const*, __CFString const*) + 476
frame #5: 0x000000019e052874 CoreText`TCGFontCache::CopyFont(__CFURL const*, __CFString const*, __CFString const*) + 144
frame #6: 0x000000019df27dcc CoreText`TBaseFont::CopyNativeFont() const + 232
frame #7: 0x000000019df8ee64 CoreText`TBaseFont::GetInitializedGraphicsFont() const + 152
frame #8: 0x000000019df26d70 CoreText`TBaseFont::CopyVariationAxes() const + 296
frame #9: 0x000000019df2d148 CoreText`TDescriptor::InitBaseFont(unsigned long, double) + 768
frame #10: 0x000000019df21358 CoreText`TDescriptor::CreateMatchingDescriptor(__CFSet const*, double, unsigned long) const + 604
frame #11: 0x000000019df251f8 CoreText`CTFontCreateWithFontDescriptor + 68
frame #12: 0x00000001bff8dfb8 WebCore`WebCore::createCTFont(__CFDictionary const*, float, unsigned int, __CFString const*, __CFString const*) + 124
frame #13: 0x00000001bff8e8bc WebCore`WebCore::FontPlatformData::fromIPCData(float, WebCore::FontOrientation&&, WebCore::FontWidthVariant&&, WebCore::TextRenderingMode&&, bool, bool, std::__1::variant<WebCore::FontPlatformSerializedData, WebCore::FontPlatformSerializedCreationData>&&) + 228
frame #14: 0x00000001c128eef4 WebKit`IPC::ArgumentCoder<WebCore::Font, void>::decode(IPC::Decoder&) + 1352
frame #15: 0x00000001c1333ca4 WebKit`std::__1::optional<WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>> IPC::ArgumentCoder<WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>, void>::decodeIPC::Decoder(IPC::Decoder&) + 480
frame #16: 0x00000001c1333a5c WebKit`std::__1::optional<WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>> IPC::Decoder::decode<WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>>() + 28
frame #17: 0x00000001c1333804 WebKit`std::__1::optional<std::__1::pair<WebCore::AttributedString::Range, WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>>> IPC::Decoder::decode<std::__1::pair<WebCore::AttributedString::Range, WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>>>() + 156
frame #18: 0x00000001c121f368 WebKit`IPC::ArgumentCoder<WebCore::AttributedString, void>::decode(IPC::Decoder&) + 172
frame #19: 0x00000001c121f124 WebKit`std::__1::optionalWebCore::AttributedString IPC::Decoder::decodeWebCore::AttributedString() + 28
frame #20: 0x00000001c12594ec WebKit`IPC::ArgumentCoder<WebCore::DictionaryPopupInfo, void>::decode(IPC::Decoder&) + 76
frame #21: 0x00000001c12d0660 WebKit`std::__1::optionalWebCore::DictionaryPopupInfo IPC::Decoder::decodeWebCore::DictionaryPopupInfo() + 28
frame #22: 0x00000001c12ceef0 WebKit`IPC::ArgumentCoder<WebKit::WebHitTestResultData, void>::decode(IPC::Decoder&) + 1292
frame #23: 0x00000001c1338950 WebKit`std::__1::optionalWebKit::WebHitTestResultData IPC::Decoder::decodeWebKit::WebHitTestResultData() + 28
frame #24: 0x00000001c1ec7edc WebKit`WebKit::WebPageProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 31392
frame #25: 0x00000001c1fb8f28 WebKit`IPC::MessageReceiverMap::dispatchMessage(IPC::Connection&, IPC::Decoder&) + 272
frame #26: 0x00000001c19ab2c0 WebKit`WebKit::WebProcessProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 44
frame #27: 0x00000001c1fb3254 WebKit`IPC::Connection::dispatchMessage(WTF::UniqueRefIPC::Decoder) + 252
frame #28: 0x00000001c1fb3768 WebKit`IPC::Connection::dispatchIncomingMessages() + 576
frame #29: 0x00000001b9ab90c4 JavaScriptCore`WTF::RunLoop::performWork() + 204
frame #30: 0x00000001b9ab9fec JavaScriptCore`WTF::RunLoop::performWork(void*) + 36
frame #31: 0x000000019b8cc8a4 CoreFoundation`CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 28
frame #32: 0x000000019b8cc838 CoreFoundation`__CFRunLoopDoSource0 + 176
frame #33: 0x000000019b8cc59c CoreFoundation`__CFRunLoopDoSources0 + 244
frame #34: 0x000000019b8cb138 CoreFoundation`__CFRunLoopRun + 840
frame #35: 0x000000019b8ca734 CoreFoundation`CFRunLoopRunSpecific + 588
frame #36: 0x00000001a6e39530 HIToolbox`RunCurrentEventLoopInMode + 292
frame #37: 0x00000001a6e3f348 HIToolbox`ReceiveNextEventCommon + 676
frame #38: 0x00000001a6e3f508 HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 76
frame #39: 0x000000019f442848 AppKit`_DPSNextEvent + 660
frame #40: 0x000000019fda8c24 AppKit`-[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 688
frame #41: 0x000000019f435874 AppKit`-[NSApplication run] + 480
frame #42: 0x000000019f40c068 AppKit`NSApplicationMain + 888
frame #43: 0x00000001ca56a70c SwiftUI`merged generic specialization <SwiftUI.TestingAppDelegate> of function signature specialization <Arg[0] = Existential To Protocol Constrained Generic> of SwiftUI.runApp(__C.NSResponder & __C.NSApplicationDelegate) -> Swift.Never + 160
frame #44: 0x00000001ca9e09a0 SwiftUI`SwiftUI.runApp<τ_0_0 where τ_0_0: SwiftUI.App>(τ_0_0) -> Swift.Never + 140
frame #45: 0x00000001cad5ce68 SwiftUI`static SwiftUI.App.main() -> () + 224
frame #46: 0x0000000105943104 MyApp Dev.debug.dylib`static MyMacApp.$main() at :0
frame #47: 0x0000000105943c9c MyApp Dev.debug.dylib`main at MyMacApp.swift:24:8
frame #48: 0x000000019b464274 dyld`start + 2840
In SwiftUI for macOS, how can I animate the transition from one Tab to another Tab within TabView when the selection changes?
In AppKit, we can do the following:
let tabViewController = NSTabViewController()
tabViewController.transitionOptions = [.crossfade, .allowUserInteraction]
How can I achieve the same crossfade effect when using TabView?
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.
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?
When I add a simple menu to the dock via the NSApplicationDelegate method -applicationDockMenu: and run the app from Xcode it doesn't work.
-(NSMenu*)applicationDockMenu:(NSApplication*)sender
{
NSMenu *dockMenu = [self buildDockMenu];
if (dockMenu != nil)
{
NSLog(@"Returning dock menu.");
return dockMenu;
}
else
{
NSLog(@"Not ready to build dock menu");
return nil;
}
}
When I run the app, my main app window shows up but nothing logs out in -applicationDockMenu: until I click outside my app's window (so if I click the desktop background, or a Finder window, or whatever). Then after I click outside my app's main window this logs out:
Returning dock menu.
The "Not ready to build dock menu" message does not log out.
But...when I right click on the dock icon, the menu doesn't show up.
But if I stop the app from Xcode and just run it not attached to the debugger, the dock menu does show up. But this makes the debugging/testing situation not ideal.
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?
All the threads only contain system calls. The crashed thread only contains a single call to my app's code which is main.swift:13.
What could cause such a crash?
crash.crash
I’m building a macOS app using SwiftUI with a WindowGroup(id: "rootWindow") for the main UI. The app shows a countdown timer, and the timer continues to run even after the user closes the main window (clicks the red "X"). When the timer reaches 0, I want to automatically reopen that window and bring the app to the front.
I’m currently using the following code to bring the app to the foreground and show the window when the app is still open (but not focused/resign active state):
NSApp.activate(ignoringOtherApps: true)
NSApp.windows.forEach { window in
if window.identifier?.rawValue.starts(with: "rootWindow") {
window.makeKeyAndOrderFront(nil)
}
}
However, this doesn’t work when the window has been closed. At that point, NSApp.windows no longer contains my SwiftUI window, and I have no reference to recreate or reopen it. I also cannot use openWindow environment value as it requires a view.
How can I programmatically reopen a SwiftUI WindowGroup window after it’s been closed, without requiring any user interaction (like clicking the Dock icon)?
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.
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)
I would like to provide a default filename when saving a document depending on the document data. I thought I could do so by overriding NSDocument.prepareSavePanel(_:) and setting NSSavePanel.nameFieldStringValue, but simply implementing that method seems to hide the file format popup button shown by default (see image). Calling super doesn't help.
Is it possible to set a default filename and keep the file format popup button? On macOS 15, I can toggle NSSavePanel.showsContentTypes, but how about macOS 14 and older?
Xcode has been downloading many similar crash reports for my app for some time now, related to an index out of range runtime exception when accessing a Swift array. The crashes always happen in methods triggered by user input or during menu item validation when I try to access the data source array by using the following code to determine the indexes of the relevant table rows:
let indexes = clickedRow == -1 || selectedRowIndexes.contains(clickedRow) ? selectedRowIndexes : IndexSet(integer: clickedRow)
I was never able to reproduce the crash until today. When the app crashed in the Xcode debugger, I examined the variables clickedRow and selectedRowIndexes.first, which were 1 and 0 respectively. What's interesting: the table view only contained one row, so clickedRow was effectively invalid. I tried to reproduce the issue several times afterwards, but it never happened again.
What could cause this issue? What are the circumstances where it is invalid? Do I always have to explicitly check if clickedRow is within the data source range?
Hello,
I am trying to match the font and the position of the date and time displayed.
This is what it looks like:
.font: NSFont.systemFont(ofSize: NSFont.systemFontSize, weight: .regular),
.foregroundColor: NSColor.labelColor,
.baselineOffset: 0.5
It looks great on built-in display, but on external 4K it is incorrect.
The baselineOffest is unnecessary, and the font looks skinny in comparison.
Can anyone comment on such issue?
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)
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.)
When my macOS Cocoa app displays a modal alert with beginSheetModal(for:completionHandler:), VoiceOver sometimes seems to focus on an "illegal" upper level, where any attempts at navigation will give the unhelpful response "Alert, dialog", until you "drill down" with VO + shift + down or switch apps. After that, things will work as expected.
Is this a known bug? Does it happen to anybody else, or am I doing something wrong?
Hi,
In mac os swift ui application when i set window.isReleasedWhenClosed and when i close the window the app is getting crashed with exc_bad_access. but when i leave it to default value the app is not crashing. for some windows setting window.isReleasedWhenClosed to true is woking properly when closing the windows. But for some windows it is crashing. If i dont set it to true the window is not removed from NSApplication.shared.windows sometimes. I am confused about setting isReleasedWhenClosed to true
Could someone calrify on this please.
thank in advance.