8

I have an app that sits in the menu bar, and when you click on the button it presents an NSPopover.

That all works fine, but I want an NSSearchField to become the first responder, so a user can then type straight in the box.

However, if the app was not already selected, it won't work. And although it looks like the search box is active, the keyboard input goes to the previous app that was open, because technically it is still the active window.

I've seen a few questions about things like this, but they all require objc methods like makekeyandorder, or just stuff that won't apply to an NSPopover.

So my question is - Is there a way that when the button is pressed, I can then force the application to become the active app?

I had a wild guess and tried NSApplication().sharedApplication().becomeFirstResponder(), but no luck.

If there's another way to do it, that I've just missed completely, then please let me know!

Christopher Hannah
  • 1,718
  • 2
  • 14
  • 23

3 Answers3

14

I fixed it myself in the end, it turned out to be a very simple fix as expected.

All I needed to to was call NSApplication.sharedApplication().activateIgnoringOtherApps(true) in the ViewControllers viewDidAppear() method.

It fixes everything!

Christopher Hannah
  • 1,718
  • 2
  • 14
  • 23
  • interestingly enough, it fails to work when i build and try using xcode 8. it seems fine when other apps have the focus. go figure... – joe Jan 22 '17 at 00:13
  • 6
    Thanks! Works great. In Swift 4 the syntax has changed slightly: `NSApplication.shared.activate(ignoringOtherApps: true)` – Savjee Nov 27 '17 at 08:06
  • I wonder how Dropbox app does the similar thing but without activating the app. When I move mouse over the Dropbox icons in the Popover, the tooltips get displayed. However, in my own app, without activation it no tooltip pops up :( – Pavel Lobodinský Aug 18 '18 at 13:03
  • @PavelLobodinský Did you find a solution for the thing you describe? I'm looking for the same behavior. – Martin Mihaylov Jun 01 '19 at 21:48
  • @MartinMihaylov No, I didn't :( Instead I designed my app's icons in a way they are understandable without any tooltips. But my humble guess is that they use a custom-made window. – Pavel Lobodinský Jun 03 '19 at 07:10
5

Even though @Christopher_Hannah answer works, the documentation clearly says,

You don’t need to send this message to make one of the app’s NSWindows key. When you send a makeKey() message to an NSWindow object, you ensure that it is the key window when the app is active.

So assuming we're in a view controller, all you need to do is

override func viewDidAppear() {
    super.viewDidAppear()

    view.window?.makeKey()
}
cnotethegr8
  • 7,342
  • 8
  • 68
  • 104
  • This, rather than `activateIgnoringOtherApps(true)` works on modern macOS (Monterey and Ventura). The other, older approach does not. – Bryan Nov 23 '22 at 05:27
0
final class HostingView<Content: View>: NSHostingView<Content> {
override func viewDidMoveToWindow() {
    window?.becomeKey()
}

}

And then use like so:

let item = NSMenuItem()
let contentView = ContentView()
let hv = HostingView(rootView: contentView)
hv.frame = NSRect(x: 0, y: 0, width: 500, height: 500)
item.view = hv

let menu = NSMenu()
menu.items = [item]
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 26 '22 at 06:55