SwiftUI ColorPicker Fails In Background (Menubar) App - macos

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.

Related

Toolbar at bottom on macOS with SwiftUI

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...).

SwiftUI macOS MenuBarExtra window doesn't appear with fullscreen apps

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
...

SwiftUI option key alternate menu items on macOS

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(...)
}

NSWindow close button is disabled

Goal: window close button to be present at all times..
I tried to use
override func viewDidAppear() {
var button = view.window?.standardWindowButton(NSWindowButton.CloseButton)
button?.enabled = true
}
But when i move from one view controller to the other, the close window button is still disabled. Note that the view controllers are presented as sheets..
How can i have the button enabled in all view controllers?

How do I add a tooltip for UIButton?

Is there a tooltip property for a UIButton instance that will display text when the mouse hovers over the button?
UIButton is an iOS class.
There is no mouse.
As there is no mouse in IOS, there is no "mouse over" or tooltip property of UIButton.
But you could try to simulate one using event handling.
If user touches the button and holds his finger in, a text is shown.
If user touches up he likes to select the button.
If user does move outside the button and ends the touch there, the tooltip is hided.
Check UIControlEvent for UIButton, i.e. "Touch Up Inside" "Touch Drag Enter" "TOuch Drag Exit"
That is true that you don't have mouse in iOS, but now that we have Catalyst which let you run ios apps on macOS, we do have mouse.
For any UIView you can use :
addGestureRecognizer(UIHoverGestureRecognizer(target: self, action: #selector(hoverEvent(_:))))
and do what you want in the implementation such as :
#objc
func hoverEvent(_ sender: UIHoverGestureRecognizer? = nil) {
switch sender?.state {
case .began, .changed:
myButton.isHidden = false
case .ended, .cancelled, .failed, .possible:
myButton.isHidden = true
default:
break
}
}

Resources