0

I have the following class

CalendarViewController.swift

import UIKit

class CalendarViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
}

and I have CalendarViewController.xib with file owner custom class is CalendarViewController.

Based on https://stackoverflow.com/a/55801259/72437

I thought if I want to create view controller from XIB, I need to perform

extension UIViewController {
    static func instanceFromNib() -> Self {
        func instantiateFromNib<T: UIViewController>() -> T {
            return T.init(nibName: String(describing: T.self), bundle: nil)
        }

        return instantiateFromNib()
    }
}

let calendarViewController = CalendarViewController.instanceFromNib()

However, if I try the following code

let calendarViewController = CalendarViewController()

I notice that calendarViewController is created from XIB! (Because it contains all the UI components from XIB)


I also notice that, if I remove CalendarViewController.xib from the project, and run same code

let calendarViewController = CalendarViewController()

again, then the view controller will not created from XIB.


May I know, why UIViewController is created from same name XIB even though empty constructor is used?

Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
  • Because `init(name:bundle)` with `nil` name will call default `init()` at some point, AND see the doc of https://developer.apple.com/documentation/uikit/uiviewcontroller/1621487-nibname or it's even described in `init(name:bundle:)` – Larme Jun 18 '21 at 10:06
  • But this doesn't explain why init will read from "CalendarViewController.xib", as the string value "CalendarViewController.xib" is not passed into init – Cheok Yan Cheng Jun 18 '21 at 10:41
  • What if init for UIViewController is in fact `init{ return [super initWithNibName:nil: bundle: nil];}` ? – Larme Jun 18 '21 at 11:01

1 Answers1

1

CalendarViewController.init() / CalendarViewController() internally invokes - init(nibName: String?, bundle: Bundle?).

It is clear that you have not specified any value for nibName. So nibName value is nil in your case.

Let's move on to the UIViewController.init(nibName:bundle:) docs. This section in the Discussion is of our interest.

If you specify nil for the nibName parameter and you do not override the loadView() method, the view controller searches for a nib file as described in the nibName property.

  1. nibName is nil in your case.
  2. You have not added any implementation for loadView() in your subclass.

This leads us to UIViewController.nibName docs -

Discussion

This property contains the value specified at initialization time to the init(nibName:bundle:) method. The value of this property may be nil.

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 point is - if you don't specify anything for nibName AND you have not implemented loadView() for your UIViewController subclass - system automatically tries to look up a matching nib file in app bundle resources.

  1. If it finds one, it will attach that view to your UIViewController.view instance.
  2. If it doesn't find one, it creates an empty UIView instance and attach that view to your UIViewController.view instance.
Tarun Tyagi
  • 9,364
  • 2
  • 17
  • 30