I am writing a SwiftUI app that uses a MenuBarExtra on macOS. When I click on the menu bar item, it displays as expected, however if I am in a fullscreen app, the window does not appear in that space; toggling to an non-fullscreen app shows it there.
Is there any way to make the window appear in fullscreen apps? In an AppKit app I'd do this when making the window:
window.collectionBehavior = [.canJoinAllSpaces, .stationary, .fullScreenAuxiliary]
Is there any way to do something like that from SwiftUI? Clicking on the menu bar item and nothing appearing is not a great user experience.
Here's my code:
#main
struct MyApp: App {
#StateObject var model = Model()
var body: some Scene {
#if os(macOS)
MenuBarExtra(model.statusString) {
ContentView()
.environmentObject(model)
}
.menuBarExtraStyle(.window)
#else
...
Related
I am building a full screen function which is supposed to hide the navigation bar and the side bar. However, I have only found a way to toggle the sidebar using NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil), which is not working well in my case because I cannot detect if the side bar is visible or not on mac (.onDisappear does not seem to be called on mac when the sidebar collapses).
Below is a simplified example of my NavigationView. I did not use NavigationSplitView because it does not work in my case unless I use some nasty hacks (which leads to crashes too).
let data: [String] = createData()
NavigationView {
List(data, id:\.self) { item in
NavigationLink {
DetailView(item)
} label: {
Text(item)
}
}
DetailView("Nothing Selected")
}
On my macOS app, I have a main toolbar at top.
However, I would like to add a new bottom toolbar at bottom (smaller).
Like that for example:
It seems there is no way to do this on macOS as on iOS:
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
// add wine
AddButton()
Spacer()
}
}
=> .bottomBar doesn't exist on macOS.
Well, just before creating a custom view/content with some buttons, I would like to be sure there is no official way to do that on macOS (with SwiftUI only...).
Context
I have an app that runs only from the macOS menubar. (The LSUIElement property in info.plist is set to YES).
Instead of a menu, this app shows an NSPopover when the menubar button is clicked. The popover holds an NSHostingView which has an extremely simple SwiftUI view:
struct PopoverContentView: View
{
#State private var color: CGColor = .white
var body: some View
{
ColorPicker(selection: $color) {
Text("Pick a Color:")
}
}
}
Problem
Clicking on the ColorPicker() does not open the macOS color picker window. The UI of the ColorPicker() button changes, to show the "selected" border state but the color-picker window never appears.
However, if I change LSUIElement to be NO and then make the app active by clicking its Dock icon (so that it takes over the menubar), THEN clicking on the ColorPicker() in the popover actually reveals the color-picker window.
Do you know of a way to force macOS to show the color-picker window for a background application?
The answer turned out to be simple. In the AppKit ViewController that opens the popover when the menubar button is clicked (PopoverController, for me), I simply did this:
extension PopoverController: NSPopoverDelegate
{
func popoverWillShow(_ notification: Notification)
{
NSApp.activate(ignoringOtherApps: true)
}
}
The ColorPicker now correctly shows the standard macOS system color panel on click.
In many macOS apps, if you open a menu from the main menubar, you can press the option key to change some of the items.
For example in Safari, I can open the File menu, and there is a "Close Tab" item. Pressing option changes it to "Close Other Tabs".
Is there a way to do this with SwiftUI?
I know how to create basic menus, but I don't see a way to detect the option key.
I think in AppKit you use NSMenuItem's isAlternate property.
struct MyApp: App {
var body: some Scene {
WindowGroup {
...
}.commands {
CommandGroup(replacing: .newItem) {
Button { newFolder() } label: { Text("New Folder") }
.keyboardShortcut("n")
...
}
}
Update to Question
I've tried a few more things, and run into two problems.
I can connect a custom object to the end of the NSResponder chain, and the system will call its flagsChanged method when the user presses the option key. However, it will not call this method when a menu is open.
Even if I find a way to observe the option key while the menu is open, changing state used to build a CommandMenu causes the menu to disappear, not rebuild and stay open.
var body: some Commands {
// If the `isOptionDown` property changes when this menu is open,
// SwiftUI doesn't change the menu, it simply closes it.
// That's not how macOS apps usually behave.
CommandGroup(replacing: .pasteboard) {
Button(...)
if optionKeyWatcher.isOptionDown {
Button(...)
} else {
Button(...)
}
Writing a Macos app. The following code just puts up a simple navigation list. Everything is fine with single clicking the Row links and displaying the Detail row.
If you double click the NavigationLink, another window opens with only the Text view on it. During my testing, there was a button to dismiss the view on the detail window, and if clicked, the original window would close leaving this stripped down view open.
I have to assume that no one else is seeing this since I cannot see anything from other people.
Does anyone have any ideas what would cause this to happen?
import SwiftUI
struct ContentView: View {
var body: some View {
HStack {
NavigationView {
List(0..<100) { row in
NavigationLink(destination: Text("Detail \(row)")) {
Text("Row \(row)")
}
}
}
}
}
}
Just use sidebar list style explicitly
List(0..<100) { row in
// ... content here
}
.listStyle(SidebarListStyle()) // << here !1
Tested with Xcode 13.2 / macOS 12.1