Multiple View Controllers and one view nib - ios

I want to achieve the following:
I have a nib which has a View Controller which is responsible for that view.
I want to write classes which inherit from this view controller and therefore share the same nib file as the base view controller but add additional specific code. I could just build a whole lot of functionality into the base view controller but then it gets messy; I really want to be able to have the same base structure with one nib and then have subclasses which add additional features.
The trouble I am having is in instantiating (in code) the subclasses using the base class's view.
I have tried [NSBundle nibWithNibName:...] and [[vc alloc] initWithNib:...] - they all give errors.
Do I set the file's owner to the base class? Do I set the view's custom view controller for the base class? How do I achieve this?
Thanks

To set up outlets from the view controller to the view or subviews, you must set the File's Owner in the nib to the view controller's class.
The actual view controller can be a subclass of the class declared as the File's Owner in the nib. But then the outlets must still be in the superclass, or you won't be able to load using the other subclass.
So for example let me call the view controller SuperVC, Sub1VC, and Sub2VC. Then:
Define all needed outlets in SuperVC
Declare File's Owner in the nib as a SuperVC
Draw all outlet connections in the nib, including view of course
Now say:
Sub1VC* vc = [[Sub1VC alloc] initWithNibName:#"nibname" bundle:nil];
Or:
Sub2VC* vc = [[Sub2VC alloc] initWithNibName:#"nibname" bundle:nil];
They will both work.

It sounds to me like you need to set the view outlet.
This is a seriously common mistake. Just control + drag from the File's Owner (the base view controller), to the top level view, and set the outlet of view.

Related

What is the relationship between the view controller on the storyboard and view controller I define through code?

When I create a view controller in the Interface Builder, I associate it with my code version of the class through selecting the appropriate name from the Identity Inspector. Is the view controller from IB a subclass of the class I coded?
As far as I can tell the view controller in IB is not an instance because you still have to instantiate it:
if let vc = storyboard?.instantiateViewController(identifier: "Detail") as? DetailViewController {
// use vc
}
I don't think it's a property of the code version of the view controller, DetailViewController in the above example. It's being instantiated through storyboard, which in turn is from UIStoryBoard.
Think of the storyboard as a resource file. instantiateViewController just reads that file and creates a certain UIViewController subclass, by calling its init(coder:) initialiser. After that, it creates all the views found on the storyboard, and adds it into the VC's view. How does instantiateViewController know which UIViewController subclass to create? The subclass's name is actually stored in the storyboard, exactly when you type "DetailViewController" in the identity inspector!
The View Controller you see in IB is only as much of an instance as this JSON...
{
"username": "Sweeper",
"id": 5133585
}
is an instance of this struct:
struct User {
let username: String
let id: Int
}
It's not a subclass of DetailViewController either. It's just data in a resource file.
A storyboard is a collection of scenes / vcs related to each others by a segue if exists , when you create a vc you have the option to create it completely programmatically in terms of it's layout or create it's layout inside a storyboard and then assign the vc name in identity inspector so that you use it to create instance of that vc with instantiateViewController which is linked to the layout specified in storyboard there is no super-subclass relation. the code in vc acts as the series of the vc's life cycle . think of the sotyboard part as an easy way to add the layout components like label / button with constraints to the vc's view instead of creating them programmatically that is the main difference
A view controller in the Interface Builder (IB) is not a subclass of the class you define in your .swift file. The storyboard really just helps you visually define the layout and constraints of the subviews that a UIViewController controls.
Basically what your code snippet is doing is "find the storyboard object with 'Detail' as its identifier, make sure its companion class is of type DetailViewController, and then create an instance of DetailViewController with the layout and constraints that are defined in that storyboard object".

Loading a Swift based UIViewController from Objective-C

I am attempting to load a view controller nib whose File's Owner is a Swift based UIViewController class. The view controller is very simple at this point--just a single label (lblResult) whose contents get set at runtime. When loading the view controller, I get the following error:
this class is not key value coding-compliant for the key lblResult
I know that this is usually because the File's Owner is not set in Interface Builder, however, in my case it IS set. I have verified that the IBOUtlet for lblResult is properly set and that the view property is also properly set. The only real difference here is that I am attempting to load the swift based view controller within an app written in Objective-C.
In my view controller class I am setting the UILabel property as follows:
#IBOutlet weak var lblResult: UILabel!
and setting its value as follows:
lblResult.text = "Time \(hour):\(minute):\(second)"
In my Obj-C calling class, I am instantiating the view controller as follows:
viewController = [[UIViewController alloc] initWithNibName:#"SwfViewController" bundle:nil];
The nib loads into view when no outlets are set, but when there are IBOutlets, i get the error. Any clues as to why this is happening? Thanks!
If your nib is a View and the owner is a View Controller, make sure you connect your view controller's view property to the nib view. If your nib is a view controller, instead of setting the File Owner just set the Custom Class of the view controller to SwfViewController.
Is this view controller present in your storyboard? If yes, you need to load the view controller by calling.
viewController = self.storyboard?.instantiateViewControllerWithIdentifier("SwfViewController") as SwfViewController
Make sure you set the identifier of the view controller in the storyboard to SwfViewController.
The storyboard should look something like this.

Shall I really use addChildViewController when I add a view to another view (and both these views are controlled by view controllers)?

I am building a UIViewController with a XIB. In this XIB, I have placeholders (UIView objects in the XIB) in which I place other views, from other view controllers. (By the way, I could also add these views directly, without placeholders)
I am not buiding a container view controller: I mean it's not a UINavigationController. It's a just a "usual" view controller. Imagine for example that I have a small subview in my view for a "Facebook" logo and a counter. This "facebook view" is attached to another view controller that is called when the "Facebook view" is touched. So, the "Facebook controller" definitely needs to be a #property of my "main" view controller.
In this situation, should I really use addChildViewController: and all the mechanism? Or no?
Thanks!
You should not be using a UIViewController merely to "fish" into a .xib file for you to obtain the view. If that's all you want it for, don't do that. Just load the nib and pull out the view, directly:
NSArray* objects = [[UINib nibWithNibName: #"MyNib" bundle: nil]
instantiateWithOwner:nil options:nil];
UIView* v = (UIView*)[objects firstObject];
But if you are using a UIViewController in conjunction with this .xib file for some other reason, e.g. to keep it alive so that a button inside the .xib can send messages to this UIViewController, then absolutely you must make a proper parent-child relationship, as I describe in my book: http://www.apeth.com/iOSBook/ch19.html#_container_view_controllers
Yes, you should. By doing so, the containing view controller sends proper view controller lifecycle events to the child view controllers.
You say you aren't building a container view controller but you are. Adding the view of another view controller to another view controller is the definition of a container view controller.
See the "Implementing a Container View Controller" section of the docs for UIViewController on the proper sequence of method calls you should make. It's more than just calling addChildViewController.
You can instantiate your insider ViewController, and just add its view (myInsiderViewController.view) to your main viewController:
[mainViewController.view addSubView:myInsiderViewController.view];

Is there a way to 'design' a custom UIView in storyboard, and use in multiple ViewControllers without adding to any specific controller?

I'm in need of a custom UIView which is to be used in multiple different ViewControllers.
Usually when I create a custom UIView, I drag an instance of UIView into my ViewController in storyboard, create a subclass of UIView, and point to this subclass in the Identity Inspector for the view. From there, I would connect the UI-objects as outlets to the header-file of the subclass.
The view I want to make now, is not supposed to be a part of any specific controller, but should be added to any controller that asks for it. A rogue UIView. I know I can create the entire UIView programmatically, and just create an instance of it, but I'd like to have it (and design it) in my storyboard.
In Storyboard, the only objects I'm allowed to drag 'outside a ViewController', are other ViewControllers.
I have never used anything other than Storyboard for iOS-developing, but I have come over tutorials which have been using the other mode, from the olden days, which looks like what I need. Is it possible to get something similar into my storyboard? Or would this require its own setup/design? If so, how?
I was also thinking of solving this with adding a 'phantom' UIViewController containing my custom View; designing it there, but instantiate it from the other controllers, but this sounds so hacky..
I'd like to do this with a UITableViewCell as well, but I guess that would have the same solution.
For your UIView, you should be creating a custom UIViewController in your storyboard and instantiate the view controller to access the view:
MyViewController *myViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MyViewController"];
// Access the view using "myViewController.view"
This is a very common practice in iOS since storyboards were presented. If you are using multiple storyboards, you should create a new instance of UIStoryBoard with:
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:#"StoryboardName" bundle:nil];
And then instantiate the view controller with this instance of the storyboard.
If you want to solely use a UIView, you should be creating a .xib file, i.e. the olden days format. To create a custom UITableViewCell, I would absolutely use a .xib file. Your last option would be to create a UIView programmatically, which could be called from any place in your application. Depending on the complexity of this view, this may be a valid option.
I don't think you can create a custom UIView in storyboard. So in this case, you can create a xib file for that custom view. Then when you want use that view, just use
UIView *customView = [[[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil] objectAtIndex:0];
to get that view.

How to get a existing controller object from storyboard file?

I'm new for storyboard file in iOS.
As I know, the objects (view controller object included) that I drag into the storyboard are existing instances, not a virtual class.
However, who owns those instances in application? AppDelegate? or others?
Now I'm trying to get a controller object from storyboard file and make the view of that controller shown with popover. (I don't want to drag the relation lines among the objects completely, and use -addSubView: to implement all the sub-objects in popover view.) I think there must be a way to access the independent controller in stroyboard file.
Any suggestions?
If I implement the view controller in MyViewController class and init a new object with [MyViewController new], this would be another object that's not I want, I think.
From within your view controller you can access the storyboard and instantiate the new view controller:
NewViewController *newVC = [self.storyboard instantiateViewControllerWithIdentifier:#"MyNewViewController"];
You may then use this in your popover.

Resources