NSPopover closes automatically - cocoa

I have a xib with my view and an NSPopover with Transient behavior:
In the view controller, I have an action to control the popover like this:
#IBAction func moreClicked(sender: NSButton) {
if !moreOpen {
moreOpen = true
scriptsPopover.showRelativeToRect(sender.bounds, ofView: sender, preferredEdge: 2)
} else {
moreOpen = false
scriptsPopover.close()
}
}
When I click my button the popover appears as expected. But after 5 seconds it disappears.
I want the popover to present a number of buttons and only disappear when the user clicks one of those buttons or clicks elsewhere in the UI. Like the Autolayout Pin button in Interface Builder to mention an example.
I tried defining the behavior as Transient, Semi-transient, Application-defined. All have exactly the same result: It dismisses itself after a few seconds.
I tried implementing the popoverShouldClose delegate and returning false to let me control it. It does block the close, but when the user clicks the button to close, it just opens a new popover on top of the old. The popover also loses its arrow after I return false from popoverShouldClose, which looks weird.
Here's a recording of the annoying automatic close

See the stack trace when the popoverShouldClose method is called. You'll see the cause of this in that stack trace, and from there, try to eliminate this cause.
What I'm suspecting is that your popover is being deallocated as it isn't being strongly held by you.

Related

How to make NSPopover properly follow the mouse pointer and ignore mouse events?

I would like to display an informational NSPopover that tracks the user's mouse cursor.
For this, I am using an NSTrackingArea to update the popover's positioningRect whenever the mouseMoved event fires.
However, this has two drawbacks:
The popover follows the mouse with a slight delay. How can I reduce this delay to make the popover appear more "glued" to the mouse pointer?
When I move the mouse pointer in the direction of the popover, the tracking area's mouseExited method gets called, which causes the popover to "absorb" the mouse movement events, so that the tracking area's mouseMoved event no longer fires. How can I avoid the popover absorbing the mouse events, or at least keep forwarding these events?
This question is very similar to Any way around this NSTrackingArea quirk?, with the distinction that I am using NSPopover, so I have nothing to set ignoresMouseEvents on.
I took a look at your problem. I was not able to eliminate the lag, but it might reduce if you set popover.animates to false.
Wrong approach:
I was able to solve the mouseExited over popover issue by adding a new border (and shadowless) window on top of the other one. The trackingArea is added to the transparent window, the popover to the original one. Depending on the transparent windows level, it is above the popover and therefore they can't interfere with each other.
In the gif below you can see the results of my tests:
Here is some of my code:
The mouse tracking:
override func mouseMoved(with event: NSEvent) {
let location = self.view.convert(event.locationInWindow, from: nil)
popover.positioningRect.origin.x = location.x
popover.positioningRect.origin.y = location.y
}
The custom window:
transparentWindow.backgroundColor = NSColor.clear
transparentWindow.isOpaque = false
transparentWindow.styleMask = .borderless
transparentWindow.makeKeyAndOrderFront(nil)
Update 11/11/2016:
I just read the question in the link you provided. There is a window to set ignoresMouseEvents on. Even though NSPopover inherits from NSObject, you have a contentViewController, which holds an view object, which holds the popovers window. (as explained here)
So simply set
popover.contentViewController?.view.window?.ignoresMouseEvents = true
after the popover is shown.

tvOS: A way to have UISearchController not appear as a button?

This code
var searchController: UISearchController!
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
view.addSubview(searchController.searchBar)
getItems()
}
produces this:
Screeshot1
Note that the searchBar appears as a button stuck in the upper left (because this is a tabbed app it appears under the tab bar when first presented. The "Button" button is there to receive focus for testing).
This is how it looks after pressing the button: Screenshot2
The second image shows how I would like things to look when the search tab opens, and the way I though it was supposed to look in tvOS.
How do I get the searchController to appear as it does in the second screenshot? Many tvOS apps do this, so I know it must be possible. I have been over the documentation carefully, but I must have missed something.
A related is issue is that the collectionView below won't take focus from the searchController. One has to go back using the remote menu button to get the collectionView to focus.
How can I get the searchController to appear as it does in the second screenshot when the view appears?
How can I get the collectionView to take focus from the searchController without having to go back to the tab bar?
is I finally ran across this passage in the tvOS developer library
All of the iOS first responder mechanisms work on tvOS. This allows
developers to present a UI, visible or hidden, and then make one of
the text fields the first responder by calling the
becomeFirstResponder method on it. This action causes the text field
to put up the keyboard UI without the user having to navigate to a
text field and click on it.
so, adding
searchController.searchBar.becomeFirstResponder()
displays the inline keyboard that I wanted users to start with. However, one has to press the menu button on the remote before the focus engine kicks in again. The menu button also dismisses the keyboard and searchBar returns to its button state.
This an answer to the first of my questions. Still clueless about the second.

Can you detect when a Safari popover closes?

We're trying to have multiple popovers in our extension and we need to know when the popover closes so we can change the popover value on the button.
Is there any listener for that?
When the popover is closed, the window object receives a blur event.
So, something like this in the popover will work:
window.onblur = function () {
// do stuff when popover is closed
};

Interface Builder: determine which element has the initial focus

I have a Cocoa app with a table view and a few other controls. When the app launches and the window is shown, a blue focus ring is drawn around the table view.
How can I get rid of that focus ring? I'd like nothing to have the focus when the window first shows.
The window has initialFirstResponder binding that shows which control will be active when the window becomes active. Change the initialFirstResponder or adjust tableview settings in interface builder to hide the focus ring
The best way I've found of stopping any of the controls from being the first responder when a window is first displayed is in the window controller:
Swift 3:
class YourWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
// Wait a frame before setting the first responder to be the window itself.
// We can't just set it right now, because if the first responder is set
// to the window now the system just interprets that as meaning that we
// want the default behavior where it automatically selects a view to be
// the first responder.
DispatchQueue.main.async {
window!.makeFirstResponder(nil)
}
}
}
It's messy, and sometimes when the window loads you see the focus ring starting to appear on one of the controls for one frame, but I haven't found a better way yet.

Stopping a popover window becoming the key window

I am trying to write a Mac application which runs in the menubar, which when clicked, displays an iOS-like popover. Similar to Flexibit's Fantastical, or The Iconfactory's Take Five.
I am using INPopoverController which seems to work great.
My problem is that when the user clicks on anything in the popover view, the popover becomes the key window. Is there a way to stop this from happening?
In INPopoverWindow:
- (BOOL)canBecomeKeyWindow
{
return YES;
}
means that the popover can become a key window. You can either change it to return NO, or subclass INPopoverWindow and override that method to return NO.

Resources