NSWindowController/NSViewController "Presentation" setting in Storyboard - macos

What exactly does the Presentation option(in Attribute Inspector) do in StoryBoard for Cocoa.
It gives two options to select from
Single
Multiple
P.S When googled the title, results are related to powerpoint presentation

The presentation style affects "Show" segues. Possibly it affects other segues too, but I only tested a Show segue. I tested on OS X 10.10.5 (Yosemite) with Xcode 7.1.1.
If a window controller's presentation style is "Multiple" (the default), then a Show segue to the window controller always loads a new instance of the window controller from the storyboard. This means that you can end up with multiple instances of the window controller at once, each with its own window on the screen. By default those windows will stack on top of each other, so it won't be obvious what happened until you move or close one.
If a window controller's presentation style is "Single", and an instance of the window controller has already been loaded from the storyboard, and that window controller still exists (presumably because its window is still on screen), then a Show segue to that view controller will not create a new instance. Instead, the Show segue will bring the existing window controller's window to the front.
This behavior is useful if you want behavior like, say, Xcode's Devices window, where there can only be one such window. You create a "Devices" menu item in the Window menu in your storyboard, and connect it to the Devices window controller in the storyboard with a Show segue. Set the Devices window controller's presentation style to Single. Now the menu item will never create a second Devices window controller if one already exists.
You'll probably want to somehow set the window's excludedFromWindowsMenu property to true, so it doesn't appear twice in the Window menu (because by default it appends itself to that menu). You could, for example, use a subclass of NSWindowController that sets it:
class DevicesWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
window?.excludedFromWindowsMenu = true
}
}
View controllers also have a presentation style, because you can also connect Show segues to view controllers. A Show segue connected to a view controller automatically creates a window controller to contain the view controller at runtime. The window controller's presentation style is effectively set to the view controller's, so you get the same singleton behavior if you set the view controller's presentation to Single.
As far as I can tell, the storyboard setting has no corresponding public property or method you can use in code.
If you connect the Show segue to a storyboard reference (new in Xcode 7), then the segue ignores the presentation style of the destination window controller, and acts as if it were "Multiple". This happens even if the destination is actually in the same storyboard as the reference.

Related

How do I set the initial first responder per view in a cocoa app that switches between different views?

My Cocoa App uses one ViewController. I do not use the InterfaceBuilder On app launch a view will be created and the user can do stuff. When clicking a specific button the VC (as the view's delegate) receives a message and then replaces the view with another.
In this new view I want a specific UI element to be the first responder. So far I have not been successful.
The new view has a reference to the desired element (a subview), so the VC can pass it to the window's makeFirstResponder(:_) method.
I tried to do that in the following places:
at the end of the view's init
in the view controller's viewWillAppear()
in the VCs viewDidAppear()
in the latter two I tried:
if let myView = self.view as? MyView {
... here I try to set the UI element as firstResponder ...
}
But in any case I get the following Message:
[General] ERROR: Setting <NSTableView: 0x7f8c1f840600> as the first responder for window <NSWindow: 0x7f8c1ef0efc0>, but it is in a different window ((null))! This would eventually crash when the view is freed. The first responder will be set to nil.
So it appears that at the time I try to set the firstResponder the new view has not yet been attached to the window.
What I also tried is to override the MyView's becomeFirstResponder()method, assuming that when the view is finally presented in the window it will receive that command, but unfortunately this method does not get called.
Is there an easy way to specify an entry point for the responder chain / key view loop per view?

What is the difference between "Multiple" and "Single" for View Controller Presentation?

I've been working on an OS X app in Xcode. An option that completely perplexes me is "Presentation", with the two options "Single" and "Multiple" what does this attribute do?
So, this was actually "obvious" once I used it.
Basically, this feature causes a window to be displayed once, or multiple times if the corresponding segue in a storyboard has been triggered multiple times.
To see this in action, add a create a storyboard with a view controller in it. The place a button on the view, and an additional window controller. Create a segue between the button and the window controller to "show" the window controller.
Click on the window controller and toggle between the two Presentation options. When you run it, you will find that one case will create multiple instances of the window, while the other will create a single instance of the window.
Like I said, obvious, but had to actually use it to figure it out.

Why can't I connect my menu to my view controller IBAction?

I have a document based application. I have just created menu items in the storyboard and IBActions in my view controller. However the usual way I connect an action to a target doesn't work
-(IBAction) markAsHidden:(id)sender;
-(IBAction) markAsVisible:(id)sender;
-(IBAction) toggleHidden:(id)sender;
Here is what I see when from my menu item I press Ctrl and mouse click from menu to View Controller. It does not show my IBActions.
Any idea ? My 2 cents guess is that it has to do with the app being document based but... not really sure
Connect the menu items to the application scene's First Responder. When you connect to the application scene's First Responder, your view controller's IBActions should appear in the HUD's list of available actions instead of the action segues shown in your screenshot's HUD.
Why can't I connect my menu to my view controller IBAction?
Because your menu items and view controller are in different scenes in the storyboard. You can think of a scene as an independent graph of objects that are instantiated when the scene is loaded from the storyboard. Objects in different scenes can't be connected together in the storyboard because they're not loaded at the same time.
Just for fun, try creating an instance of your view controller in the Application Scene in your storyboard. To do that, you'll probably need to drag a plain old NSObject instance into the scene and then set its type. Once you do that, you'll find that you can drag a connection from a menu item to that view controller just as you'd expect, but you can't drag a connection to a different object of the very same type in a different scene.
Note: Once you've played around enough to convince yourself that it works, remember to delete the view controller that you added. A view controller without a view is like a duck without a quack, and a view controller and its view hierarchy should be in their own scene.
My 2 cents guess is that it has to do with the app being document based
No, it doesn't have anything to do with that. You'd have the same problem in an app that's not document-based. You'd also have the same problem if your app were .xib-based instead of using storyboards, since the controller you'd be trying to connect to would be in a completely different .xib file.
The easy solution, as Mark already described, is to use the responder chain. The First Responder proxy object is part of every scene, so you can always make connections to it. When you connect a menu item to First Responder its target will be nil, which tells NSMenu to walk the responder chain until it finds an object that responds to the menu item's action message. It then sends the message to that object.
If you are converting a project from objective C to Swift, do not make my mistake. When writing your IBAction write like this:
#IBAction func someAction(_ sender:AnyObject) {
// this will work
}
Do not omit the underscore before sender or the Interface Builder won't be able to connect to your action as in here:
#IBAction func someAction(sender:AnyObject) {
// this won't work and IB won't connect to this action
// because sender will be part of the symbol name
}

How to show different views in same window in cocoa?

Is it possible to do navigation within the same window in a mac application ?(Like it is possible in ios apps).I want to show each view in the same window instead of opening different windows on a button click.
e.g When a user clicks a button then the next page should be loaded in the same window.(The next page will have nothing in common with the current page.)
You may use Tab View for easy switching between views on a same window.
UPDATE:
You may also customize your tab view , make it tabless (In the attributes inspector set style to tabless) and use your buttons to switch between views.
You may take help from the following link : http://devcry.heiho.net/2012/01/nstabview-tutorial.html
OR
You may add or remove subviews from your window on button clicks, using
[[yourWindow contentView] addSubview: yourSubview]; // Add subview to window
[yourSubview removeFromSuperview]; //Remove subview
UPDATE:
Steps to swap between views using a tabless tab view.
Drag a NSTabView to your xib.
Set the no. of tabs in attribute inspector to no. of views you want.
Design each view of the tab as per your requirement.
Now in the attribute inspector of tabview, set style to tabless.
Now drag the buttons you want to use for swapping between views. Suppose Button0 and Button1 are for 1st and 2nd view of your tab view.
Create a IBOutlet for your NSTabView in your .h file. Bind it to the referencing outlet of you tabview.
IBOutLet NSTabView* tabview;
Set a IBAction for both your buttons in your .h class file.
In the button action method for button1, use
- (IBAction)button1clicked:(id)sender
{
[tab selectTabViewItemAtIndex:0];
}
Similarly in button2 action method use:
[tab selectTabViewItemAtIndex:1];
In this way you can have any no. of views and you may select any view on button click using
[tab selectTabViewItemAtIndex:(index of the view you want to load)];
In general you want to google for view swapping.
There are tons of examples out there. Some from Apple and lots elsewhere.
Much of it is very similar to iOS.
You need to read the docs a bit too.
Understand NSView and how to load views from nibs, how to create view objects in code, how to add a subview and how to remove a view.
There are many approaches to having different views for different reasons. The right approach is a combination of style, experience and what your app actually needs to do.
Cocoa includes NSBox, NSTabView, and lots of others. Those two can be configured to not display any visual indication that they are containers.
You will also need to understand at least a little about NSWindow to understand its content view (the root container of other views generally)

Launch window from NSView subclass in cocoa

Is it possible to launch a window in an NSView subclass by clicking a NSRect? I have tried makeKeyAndOrderFront but this doesn't work.
You can't click on a rectangle. A rectangle is just four numbers.
You can have an NSView that responds to clicks, but you should consider using NSButton instead. If you really want a custom view, you can both create the button and add it as a subview of your view programmatically. Then, set the button's target to yourself and its action to the selector of a message you'll respond to by opening the window.
One more thing: You don't launch a window. Windows aren't applications and applications aren't windows. On Mac OS X, applications have windows—always more than one (counting at least the About panel). So, you'll load the window from a nib, then make it key (respond to events) and order it front.
On that point: You probably should not have your view owning a window. Consider making a controller object to own the window instead, and having your view simply forward the message to the controller object (or even hook the button up to the controller directly).

Resources