Reputation: 1382
I have been trying unsuccessfully for a few hours trying to get an NSPopover to load an NSViewController to show from an NSStatusItem, ideally using a segue in swift.
I have managed to get the events to fire correctly from the new NSStatusItem.button implemententaiton in DP3, but I can't work out how to programatically add a segue to the NSButton. The code I have working so far is ...
class AppDelegate: NSObject, NSApplicationDelegate {
var statusItem : NSStatusItem
init() {
statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(CGFloat(-2))
// When the bug is fixed, replace the line above with this line.
//statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(CGFloat(NSSquareStatusItemLength))
}
override func awakeFromNib() {
SetupStatusItem()
}
func SetupStatusItem() {
statusItem.title = nil
var icon = NSImage(named: "Moon_Full.png")
icon.size = NSSize(width: 16, height: 16)
icon.setTemplate(true)
statusItem.button.image = icon
statusItem.button.action = "StatusItemClicked"
}
func StatusItemClicked() {
if (statusItem.button.appearsDisabled)
{
statusItem.button.appearsDisabled = false
println("Hide NSPopover here")
}
else
{
statusItem.button.appearsDisabled = true
println("Show NSPopover here")
}
}
}
This sets up the NSStatusItem, it has the right colours in light and dark mode and is logging the correct messages and toggling between active and inactive states.
The seceret sauce that I am missing is how to programatically add a segue to the statusItem.button so that it perfoms a load of an NSViewController that I have built in an NSPopover from the NSStatusItem when clicked.
Edit:
I have tried a number of scenarios with a vanilla NSButton on a ViewController
A) Hooking up a segue in interface builder ... works
B) Hooking up a segue and triggering it in code .. works, but you have to still make the connection in interface builder so that does not solve the problem at hand. ( This is API is new for 10.10 )
performSegueWithIdentifier("ShowPop", sender: sender)
C) Trying to pop a view controller in the same storyboard, is where the error is occurring.
var newView = NSView(frame: sender.frame)
var popupViewController = ViewController(nibName: "PopupViewController", bundle: NSBundle(identifier: "PopupViewController"))
presentViewController(popupViewController,asPopoverRelativeToRect: sender.frame, ofView: newView,preferredEdge: NSRectEdge.max,behavior: NSPopoverBehavior.Transient)
2014-07-17 08:57:55.238 popover[5116:2277209] -[NSNib initWithNibNamed:bundle:] could not load the nibName: PopupVIewController in bundle (null).
( I have tried naming the NIB in IB and calling by name as well as using null.
D) The same code works if I create a separate XIB, but my preference is to use the new storyboards if possible, as I want to use the new tab view as a container ...
I have created a sample project (in Objective-C to ensure that this was not a swift problem)
Thanks in for your help in advance.
Upvotes: 3
Views: 4120
Reputation: 1382
The answer I was given that works is ...
popover = NSPopover()
popover.contentViewController = NSStoryboard(name: "PopoverStoryboard", bundle: nil).instantiateControllerWithIdentifier("PopoverStoryboardVC") as NSViewController
popover.behavior = NSPopoverBehavior.Transient
Upvotes: 5
Reputation: 6566
Just don't. Invoke the popover directly using NSPopover.showRelativeToRect(...)
. I believe if you set the popover's behavior
to .Transient
, it should just close itself when you click elsewhere, but you'll need to set its delegate
property to catch the popoverDidClose
event and unhighlight the status item.
Edit:
As far as I know, there is no way to programmatically set a segue on a button. When you set a segue relationship in a Storyboard, I believe in compiled code, you just get a target/action set for you which perform the NSStoryboardSegue. So you can't do that. In view controllers, you can use presentViewController(asPopover...)
to quickly display another view controller in a popover. But when dealing with NSStatusItem and NSStatusBarButton, you don't get a view controller, so you have to do it directly.
Upvotes: 2