I created new file and check Cocoa Touch Class -> Then I set subclass as UIViewController -> I mark "Also create XIB file".
It created simple UIViewController as expected, but here is my question becuase when I called:
present(MyViewController(), animated: true)
This shows MyViewController() as I wanted but how is it possible that I'm using simple init() and Nib is loaded?
I thought that I need to use different initalizer, how Swift is doing this?
It's not "Swift" doing this. It's the implementation of the UIViewController class. The same happens with Objective-C.
Start by reading the documentation for the UIViewController init(nibName:bundle:) method and the nibName property.
From the nibName documentation:
If you use a nib file to store your view controller's view, it is recommended that you specify that nib file explicitly when initializing your view controller. However, if you do not specify a nib name, and do not override the loadView() method in your custom subclass, the view controller searches for a nib file using other means. Specifically, it looks for a nib file with an appropriate name (without the .nib extension) and loads that nib file whenever its view is requested. Specifically, it looks (in order) for a nib file with one of the following names:
If the view controller class name ends with the word ‘Controller’, as in MyViewController, it looks for a nib file whose name matches the class name without the word ‘Controller’, as in MyView.nib.
It looks for a nib file whose name matches the name of the view controller class. For example, if the class name is MyViewController, it looks for a MyViewController.nib file.
Basically, in your case you use the default initializer meaning you specified nil for the nib name. So at runtime, the nib you created with the same name as the view controller class is automatically found and used.
Related
I seen 3 different implementations of how to init a UIViewController from a xib file. The method I have been using is to create a UIViewController and let Xcode create the xib file as well and then do:
let vc = CustomViewController()
navigationController?.push(vc, true)
This works and auto-layout works and everything is great.
Why are all the other examples I see online to use:
let vc = storyboard?.instantiateViewControllerWithIdentifier("CustomViewController") as CustomViewController
or:
let vc = HomeViewController(nibName: "HomeViewController", bundle: nil)
Am I missing something with the way I have been doing things? Please note I don't use segues and storyboard navigation. I separate all my view controllers in their own xib files.
Edit:
I am using the IB to layout my UI and link IBActions back to my source file. That is why I am confused why it works and why I never see this example posted anywhere. Is Xcode doing something behind the scenes to make it work?
I believe the reason why this works is stated in the Apple Developer Docs. Since I am just calling the default init of the view controller and not overriding loadView() it looks through the xib files for the ones matching the view controller's name as explain in below:
If you use a nib file to store your view controller's view, it is recommended that you specify that nib file explicitly when initializing your view controller. However, if you do not specify a nib name, and do not override the loadView method in your custom subclass, the view controller searches for a nib file using other means. Specifically, it looks for a nib file with an appropriate name (without the .nib extension) and loads that nib file whenever its view is requested. Specifically, it looks (in order) for a nib file with one of the following names:
It looks for a nib file whose name matches the name of the view controller class. For example, if the class name is MyViewController, it looks for a MyViewController.nib file.
SourceL:
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621487-nibname?language=objc
When you load a UIViewController Subclass programmatically, any nib associated with that ViewController will not be loaded if you named any of the files a different name than your subclass.
The only way to load the nib automatically, without passing a string name to the initializer, is to make sure your x.nib and class.swift has the same name as the SubClass.
Is there a way around this?
According to the documentation, you actually can load a nib based view controller without naming it exactly the same thing. Although matching names is the most common way to do this, there's a second case, which should work even with Swift.
From the documentation:
If you use a nib file to store your view controller's view, it is
recommended that you specify that nib file explicitly when
initializing your view controller.
However, if you do not specify a
nib name, and do not override the loadView method in your custom
subclass, the view controller searches for a nib file using other
means. Specifically, it looks for a nib file with an appropriate name
(without the .nib extension) and loads that nib file whenever its view
is requested. Specifically, it looks (in order) for a nib file with
one of the following names:
If the view controller class name ends with the word ‘Controller’, as in MyViewController, it looks for a nib file whose name matches the
class name without the word ‘Controller’, as in MyView.nib.
It looks for a nib file whose name matches the name of the view controller class. For example, if the class name is MyViewController,
it looks for a MyViewController.nib file.
The nib will only load automatically if BOTH the nib and class-file are the SAME name as the class...
Sorry for perhaps noob question, guys. I'm new to iOS development.
How can I create view controller that is defined in XIB ?
When you create new class inherited from UIViewController in Xcode via "New File" and mark with XIB checkbox it creates xib file that doesn't have viewController element. But rather it has placeholder which points to UIViewController which is defined in the code.
But I see that you can create controller itself in Interface Builder and specify customClass for it.
Unfortunately it doesn't work for me. I get loaded the "EmbeddedViewController" nib but the view outlet was not set error in run-time. But I believed view property must be set by resource loader automatically since everything is defined via XIB. It works that way for TableView and its cells for example.
Could you please provide example how to do it ?
I created sample project: https://github.com/cppexpert/SampleWithNib
The controller in question is EmbeddedViewController
Create one Xib per UIViewController
Each Xib have File's Owner object for you to set UIViewController class there. Click File's Owner and choose EmbeddedViewController on it's class
Then drag main UIView not UIViewController class to there, then hook up this view with file's owner as view. UIViewController just use to drag to StoryBoard base project.
https://github.com/lequysang/gitfiles02/blob/master/SampleWithNib-master.zip
Turned out these controls exist in IB for Storyboard projects.
When you create a view controller with xib via "new file", Xcode generates an UIView and connects it with view outlet automatically. Seems like you changed something after Xcode generated the xib file. In that case you need to connect a view to the view outlet manually.
If you create a new view in xib.set the file owner to your custom class.
Or if you simply want to create a view.
Then
NSArray* test1 = [[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil];
self.myViewFromNib = [test1 objectAtIndex:0];
Where myViewFromNib is your view object and "View" is your nib name.
Here's the deal: I'm using the DEFacebookComposeViewController which is a custom subclass of a UIViewController. This subclass is using an xib to setup the user interface.
Now I want to add some additional GUI elements to DEFacebookComposeViewController so I figured I would create a subclass named something like MySubclassDEFacebookComposeViewController : DEFacebookComposeViewController and create a xib.
However the xib MySubclassDEFacebookComposeViewController.xib is not showing any of the GUI elements from DEFacebookComposeViewController.xib which I thought it would do since it's a subclass of that class.
So basically I'm just wondering what is the correct approach in adding new elements to a subclassed UIViewController and xib?
Copy the DEFacebookComposeViewController.xib and rename it to MySubclassDEFacebookComposeViewController.xib and edit it to your likings.
A UIViewController will look by default for a nib/xib with the same name (even without the controller part at the end) when created
From the docu of UIViewController:
If you use a nib file to store your view controller’s view, it is
recommended that you specify that nib file explicitly when
initializing your view controller. However, if you do not specify a
nib name, and do not override the loadView method in your custom
subclass, the view controller searches for a nib file using other
means. Specifically, it looks for a nib file with an appropriate name
(without the .nib extension) and loads that nib file whenever its view
is requested. Specifically, it looks (in order) for a nib file with
one of the following names:
If the view controller class name ends with the word “Controller”, as
in MyViewController, it looks for a nib file whose name matches the
class name without the word “Controller”, as in MyView.nib. It looks
for a nib file whose name matches the name of the view controller
class. For example, if the class name is MyViewController, it looks
for a MyViewController.nib file.
So you need to specify the nib you want to use or name it after the view controller. Since you want to add/modify the old nib you need a copy of it.
This is assuming that DEFacebookComposeViewController.xib does not have any IBOutlets by default. If it has you can modify the existing nib in viewDidLoad. Adding more subViews can always be accomplished aften the view is loaded regardless if there are IBOutlets.
I thought that I should never call [super loadView] but something is confusing me.
In description of loadView (UIViewController Class Reference) it is said that "Your custom implementation of this method should not call super.",
but in ZoomingPDFViewer example that they gave, loadView implementation (ZoomingPDFViewerViewController) is calling [super loadView].
I have tried to call it from my loadView method and it works ok, but I just don't understand then what does it mean to not call super.
You definitely should not be calling [super loadView]. I'd say you found a bug in the ZoomingPDFViewer example.
You override loadView when you want to programatically create the view hierarchy for your view controller (not using a xib).
As you pointed out, the docs clearly state that you should not call super.
Your custom implementation of this method should not call super.
I assume this is to avoid loading both from a xib and programatically creating a view as this method is used by the base to load a view from a xib:
If the view controller has an associated nib file, this method loads
the view from the nib file.
Note also that even if during allocation of your UIViewController object you pass nil for the nibNameOrNil parameter that the UIViewController implementation of loadView will try to load any xib with the associated class name in it.
A view controller has an associated nib file if the nibName property
returns a non-nil value, which occurs if the view controller was
instantiated from a storyboard, if you explicitly assigned it a nib
file using the initWithNibName:bundle: method, or if iOS finds a nib
file in the app bundle with a name based on the view controller’s
class name. If the view controller does not have an associated nib
file, this method creates a plain UIView object instead.
The real intent of this method is to give you full control of building the view hierarchy without relying on the built in xib loading mechanism.:
You can override this method in order to create your views manually.
Personally, I override loadView if: 1.) The xib I would make for it is really trivial or 2.) The layout of the control is very dynamic, so creating a xib with a static layout has little benefit.
NSViewController tries to initialize a view from a nib in -loadView. Since your nib name is not set for your controller, it will just give you a self.view = nil; I would assume UIViewController works the same way.
So it should be safe, but you it's completely unnecessary.
If you dont have a view created in your IB, then you should call [super loadView] in your code to give a view to your program.
In case of your custom views, you are suppose to create a view with the interfaz builder, so you dont need to call it.
If you create your ViewController programmatically, you could call super.loadView() instead of self.view = UIView(frame: UIScreen.main.bounds) at the beginning of override func loadView().
However, do NOT call self.view before super.loadView(), since the former will trigger the latter if view did not been loaded.
I don't think it's a good idea to explain Apple's documentation like a robot.