I'm learning Cocoa, and
I'm trying to understand the logic (behind the scenes) of the IB.
1) On a new xcode project (cocoa/obj-c) the storyboard starts with a
predefined View Controller (VC) which is "associated" with files ViewController.{h,m}.
2) I add a Push Button with name "Switch" to the the View.
3) I add a Second View Controller (VC2)
4) I connect (click-drag) "Switch" --> VC2 which create a "segue" VC-->VC2.
So far so good. If I click "Switch" on VC, the window of VC2 appears.
5) Now I want to add a label with name "Foo" to VC2 and connect
"Foo" to some IBOutlet in my code.
I'm guessing that I need to create a new class "ViewController2"
which inherits from NSViewController and make the connection
VC2 <--> ViewController2
in such a way that I can click-drag from "Foo" to the interface
of ViewController2 in ViewController2.h in order to create
an IBOutlet.
Question (finally): how do I make the connection
VC2 <--> ViewController2?
More generally: is there a good reference for understanding
the logic behind IB?
You are correct that you have to create a NSViewController subclass for the second view controller. The next step to take is to set the second view controller's class to your subclass. Set the view controller's class using the identity inspector.
After setting the second view controller's class to your subclass, you should be able to create outlets and make connections from user interface elements in the second view controller to that view controller's source code file.
Related
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
}
I created an empty xib, I dragged a UITableViewController, set Custom class to my class that extends from UITableViewController at both the UITableViewController and File's Owner.
When I run, it says:
'A view can only be associated with at most one view controller at a time! View ; layer = ; contentOffset: {0, 0}> is associated with . Clear this association before associating this view with .'
At connections inspector I see that the UITableViewController element has:
tableView - Table View
view - Table View (greyed)
dataSource - Table View
delegate - Table View
First Responder has nothing.
File's Owner has:
tableView is not attached to anything
view - Table View
If I remove the view connection at File's Owner, it claims that the view outlet is not set.
It is difficult to imagine what happens based on the information provided. However:
The error message says that you have a UIView, and you try at runtime to connect this UIView to two UIViewControllers, which is not possible.
If you just subclass a UITableViewController, without adding properties or implementing methods, then setup a UITableViewController in an empty storyboard, and set the class of this UITableViewController to your subclass, you have only a single UITableViewController, and you will not get this error.
So somewhere in your project you must instantiate a second UITableViewController programmatically, and set its UIView property (which is a UITableView) to the UIView of the other UITableViewController.
You should ensure that you have only a single UITableViewController, namely your custom subclass.
I guess the correct answer is DON'T drag UITableViewController to the interface builder. Have 2 IBOutlets connected, one for the view, one for the table. DO drag a UITableView instead.
Hope this helps other people.
In the new xcode 5 how do in interface builder you use to have a drop down menu there to connect your storyboard viewcontrollers to your classes how is this done now?
Unless I'm misunderstanding what you're asking, it's still there. Here are the steps to assign a custom view controller class to your view controller:
Choose your view controller in the list of scenes on the left side.
Choose the Identify Inspector on the right side.
Choose the custom view controller class from the Class list.
1、manual creating a Cocoa Class to the project
2、open the story board, draw a view controller from tool box to the storyborad
3、click the controller you draw into the storyboard, and specify the 'Customer Class' to the class you create in step 1
4、if you use XIB, nibName could be the controller name, else if you use StoryBoard, you need set a storyboard id for your controller in your storyboard panel, and you can use the id and the storyboard in your superController when you want to new a controller
// XIB
var viewController = UIViewController(nibName: "ViewController", bundle: nil)
// StoryBoard
var viewController = storyboard.instantiateViewController(withIdentifier: "The-Storyboard-ID-You-Set") as! ViewController
i am working on storyboards which has couple of views on first view a condition is placed i want if the condition satisfies then only navigation should happen
For this i have used Custom segue but no matter my condition satisfies or not it navigates to new view.
I have created method in custom segue class
- (void) perform{
NSLog(#"source %#",self.sourceViewController);
NSLog(#"dest %#",self.destinationViewController);
UIViewController *sVC=self.sourceViewController;
UIViewController *dVC=self.destinationViewController;
[sVC.navigationController pushViewController:dVC animated:YES];
}
I want to set condition if result is 1 then only it should navigate. Woul prepareforsegue or initwithsegue provide me any help
Are you saying that you only want to perform the segue if a condition is true?
If so, instead of creating the segue directly from a control or table cell, create a triggerless segue. A triggerless segue has the view controller as its source, and it won't ever fire automatically. Instead you can fire it programmatically any time you like, including from an IBAction.
To create a triggerless segue, start control+dragging the segue from the containing view controller icon in the scene dock at the bottom of the scene. Drag to the destination scene like normal, and pick the segue type. Select the segue, and in the inspector, choose a segue identifier.
At runtime, when you want to perform the segue, invoke -[UIViewController performSegueWithIdentifier:sender:]. You can pass any object you'd like for the sender, including nil. If the sender has no use to you, pass nil.
So, in summary:
Create a triggerless segue from the view controller to the destination scene
Set an identifier for the segue in the inspector
At runtime, and form code, call -[UIViewController performSegueWithIdentifier:sender:] when you want to trigger the segue.
I have a horizontal NSSplitView which sits on top of right sub view of another vertical NSSplitView. I have delegate methods to constraint the left sub view from resizing. But the same delegate methods are never invoked for the horizontal NSSplitView sitting on top. What can be done to achieve this?
If the delegate method is fired for the Left Split view controller but not for the Right split view controller, then most likely you haven't set the delegate for the Right Split View Controller.
If you create the right split from an Xcode template, then it will be created in Interface builder from a nib file. Open Interface builder, then right-click drag and connect the delegate property to the object that you want to be the delegate.
If the Right split view controller object isn't created in a nib, then you will need to connect this up in code.