How are .xib files being automatically associated and loaded with my ViewController? - ios

I'm using Xcode 8 and Swift, not using Storyboards.
I hit Add->Cocoa Touch Class and check the box to generate a XIB.
I now have FooViewController.swift and FooViewController.xib. The xib has the class set under File Owner. As far as I can tell this is the only association between the XIB and the class. The generated class doesn't contain anything that loads a XIB.
I can create and display this view controller like so (from some other ViewController):
let foo = FooViewController();
self.navigationController!.pushViewController(foo, animated: true);
This works and it loads complete with the UI. What I don't understand is why. Most guides and tutorials online suggest that the ViewController needs to manually load a XIB.
What is happening in the default initializer for FooViewController?
Where is the XIB loading happening?
What if I want to override it and load the XIB myself? (e.g. maybe I want to use different XIBs for different devices. Not the best practice, I know, just an example.)

Back from the day in Xcode 4.5 when we used to create a Universal Application i.e.
for
iPhone
iPad
There used to be code in didFinishLaunchingWithOptions in AppDelegate Class to load different XIBs for target devices, so it did the same as you asked above. But given the evolution of Xcode and Cocoa Framework. It has come a long way from that where the compiler does a lot for us automatically.
Here in this specific scenario:
we have provided the file owner to XIB file, so when you are trying to present it, it goes ahead and searches in navigator/file available if a XIB is available for it, if it finds such a file, it loads the UINib from the bundle and loads it
if it doesn't it goes to the code to find if there is any
programatic view being created while we do super.viewDidLoad()
once it doesn't find anything, it generates a blank view for the same view controller.
If we look closely in the XIB file, there is a view outlet attached with main view available in the XIB but we have created no such outlet, so its connecting an autogenerated outlet for VC Source file with the view in XIB. If it makes sense.
Also please find some code below for reference from tableView Context:
let newAccountTableCellNib:UINib = UINib(nibName: "AccountTableCell", bundle: nil)
tableView.registerNib(self.newAccountTableCellNib, forCellReuseIdentifier: "AccountTableCell")
This should solve your query. Cheers!!

Related

What is the difference between the different ways to navigate to a new UIViewController?

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

UIViewController defined in a xib file doesn't load in the storyboard

I'm trying to load a subclass of a UIViewController with its views defined in a xib file into a storyboard. Let's call it a NibViewController.
The point of this approach is to reuse the same ViewController in multiple screens of the app.
I know it's possible to do it manually in the code, but I'm looking for a solution in the storyboard. I've tried suggestions from other topics like this one, but nothing worked. The ViewController is correctly displayed in the simulator but not in the storyboard. Here is the code: https://github.com/srstanic/NibViewControllerInStoryboard
and here is the screenshot:
Am I mistaken to expect the contents of the NibViewController to appear in the storyboard?
Am I mistaken to expect the contents of the NibViewController to appear in the storyboard?
Yes, you are mistaken. Your app is working perfectly so you should stop worrying and just proceed.
By deleting the view from the view controller in the storyboard, you have specifically instructed the storyboard: "Do not make a view for this view controller. At runtime, the view should come from the xib file, not from you."
And that is exactly what does happen at runtime. So just design your interface in the xib file and all will be well.

UIViewController inside XIB?

Currently I'm struggling with creating a subclass of UIViewController or UINavigationController with XIB file as a view.
When I create everything from the Xcode's menu (New File -> Class -> With Nib... etc.) I get a XIB but only with a plain UIView but I want UIViewController instead.
I read somewhere that XIBs are only for a views and you have to handle controller in code, is it true? Because as you can read here it's possible to insert Navigation Controller component into XIB. But I have one problem with the code from this tutorial - I get empty view with empty UINavigationBar. When I do the same with regular View Controller I get info abut this controller being used more than once...
I'm not trying to force Interface Builder to do something unusual but I want to know if this is possible (it would be easier and nicer to modify view controller component insted of a content view)? And if it is, how to achieve this?
I have just checked to confirm whether it is possible and to my surprise it is! You can have UIViewControllers inside Xibs
The test was done in XCode 10.1, Swift 4.2.
I have never used it before, but i thought since it gives you the option from the item library to pick view controllers, it has to be possible. I have added one to my xib, and just like in the storyboards, i have linked it with class, set IBOutlets and IBActions and it all worked perfectly fine.
The key thing is to instantiate it like this:
// Method inside the `UIViewController` you want to present our view controller from xib
// The xib file is `XibViewController.xib` and it has only one item inside - `UIViewController` with custom class set to `XibViewController`
guard let xibViewController = UINib(nibName: "XibViewController", bundle: nil).instantiate(withOwner: self, options: nil).first as? XibViewController else { return }
present(xibViewController, animated: true)
here you can find my test project: https://github.com/stoqn4opm/XibViewController
An XIB file is used for building content that is viewable on a screen. A UIViewController is not viewable. It instead owns a view (which is viewable) created from an XIB or from code.
I think from what you are trying to do is use a storyboard which lets you visually layout your UIViewControllers to define there segue from each other (in your case in a Navigation Controller) which means show the next UIViewControllers view and put it in the UIViewController hierarchy.

How do I know which xib is the first one to load?

I am researching a huge Xcode project, how do I know which xib is the first xib be loaded by IOS?
In main.m, I can only know the main delegation file, but how to find the main.xib.
BTW, I search the {project}-info.plist, but found nothing.
Addtion, I know which file is the rootviewcontroller.
From your description the project doesn't use storyboard(s), so the initial window and root view controller are created by the app delegate. This may or may not use an XIB file (you'd need to check the implementation if the view controller class).

How can I rewire a MainView-iPad.xib to the viewController?

I am porting my Phone app to iPad.
I changed my target's "Devices" to Universal under "Summary".
I put the conditional (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad) to determine whether the app is running on iPad.
I created a new XIB dedicated for iPad and placed a view in there.
How can I rewire the IBOutlets back into the iPad version of the XIB?
Thanks
You probably need to define the class of the File's Owner:
Since you create a new XIB, it should be set to UIViewController, just select from the list your UIViewController's subclass.
The new image:
Just a small detail:
Avoid using this kind of stuff: (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad). It completely destroys your application architecture when creating a version for iPad and iPhone of the same application, which share the same code. Just sub-class the class you want. Keep the common code on the parent and the specifics in each sub-class.
select "Identity inspector" window on new Xib.
change class name to your viewController
then select file owner and your outlet automatically show up
there is the Controller class is same for both Xib
You don't need to rewrite any IBOutlets. But just assign the proper class to the xib's file owner and all the IBOutlets of that class is available to you and as you have used them in iPhone xib use them for iPad's xib.
Happy Coding :)

Resources