6

When I try to create an instance of a UIViewController in Swift, all the inherited initialisers are unavailable, even though I didn't define any designated inits in the view controller (or anything else, FWIW).

Also, if I try to display it by making it the root view controller, it never gets displayed:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.

        window = UIWindow(frame: UIScreen.mainScreen().bounds)
        window?.makeKeyAndVisible()
        window?.backgroundColor = UIColor.greenColor()



        let vc = ImageViewController()
        window?.rootViewController = vc

        return true
    }

The code for the view controller is just Xcode's template:

import UIKit

class ImageViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

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

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */

}

Anybody knows what's going on????

cfischer
  • 24,452
  • 37
  • 131
  • 214
  • Have you tried implementing required init(coder aDecoder: NSCoder!)? – Dreaming In Binary Nov 26 '14 at 14:03
  • I just did it, and then that's the only init it will recognise. However, I don't need it. I want self.init(nibName: nil, bundle: nil). For some reason, all the inherited initialisers seem to be missing even though I didn't add a designated init to the subclass of UIViewController – cfischer Nov 26 '14 at 14:13
  • The problem is you're creating a nil vc. Are you using storyboards or nibs? – Lyndsey Scott Nov 26 '14 at 14:15
  • I'm using nibs, but it doesn't look like a nil: po vc 0x00007fa742b00000 { UIKit.UIViewController = { UIKit.UIResponder = { ObjectiveC.NSObject = {} } } } – cfischer Nov 26 '14 at 14:16
  • nil was the wrong word... I meant a vc without an interface. – Lyndsey Scott Nov 26 '14 at 14:25

3 Answers3

7

As you've pointed out: As long as the nib name matches the class name in objective-C, even if you don't specify a nib name when initializing the view controller, the view controller will still look for the nib file whose name matches the name of the view controller class.

But for some reason (perhaps it's a bug), this is not the case in Swift.

Instead of writing:

let vc = ImageViewController()

You have to explicitly specify an interface when initializing the view controller:

let vc = ImageViewController(nibName: "nibName", bundle: nil)
Lyndsey Scott
  • 37,080
  • 10
  • 92
  • 128
  • 1
    OK, now it kinda works. :-) If I call the nibName:,bundle: initializer explicitly (even though autocomplete won't recognise it) AND I pass the name of the xib file, it works. However, that's not the expected behaviour: If I just call init or init(nibName:nil, bundle:nil) it should pick up the name of the xib file by looking at the name of the class. And it doesn't. :-( – cfischer Nov 26 '14 at 14:31
  • @cfisher But where is your init? – Lyndsey Scott Nov 26 '14 at 14:33
  • I didn't define ANY inits in the view controller. The code is exactly the one provided by Xcode's template. It's the one included at the end of the question. I'm calling it from my ApplicationDelegate, in the application did finish loading with options method. – cfischer Nov 26 '14 at 14:39
  • @cfisher OK, then that's your problem. No matter what, whether it's in your app delegate or in a custom init you 100% have to explicitly pass/include the name of your xib file. It's not true that "it should pick up the name of the xib file by looking at the name of the class." Even Apple lists the above method as the "designated initialized for the view controller class" in their docs: https://developer.apple.com/library/ios/Documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/instm/UIViewController/initWithNibName:bundle: – Lyndsey Scott Nov 26 '14 at 14:46
  • init(nibName nibName: String?, bundle nibBundle: NSBundle?) is indeed the designate initialisers for UIViewControllers. However, you do NOT have to specify a name for the nib file, unless the name of the nib file is different from the name of the class. – cfischer Nov 26 '14 at 14:59
  • From the Apple Docs: "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. " https://developer.apple.com/library/ios/Documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/instp/UIViewController/nibName – cfischer Nov 26 '14 at 14:59
  • That code, if written in Objective C, compiles and runs fine. So there's something weird going on when using Swift... – cfischer Nov 26 '14 at 15:00
  • @cfisher Hm... I'm looking into it. – Lyndsey Scott Nov 26 '14 at 15:21
  • @cfisher Yeah, I've gotten the same results with my own code and have updated my answer accordingly for future answer seekers. Perhaps you should submit a bug report to Apple. – Lyndsey Scott Nov 26 '14 at 15:55
  • Thanks anyway, you gave me at least a workaround! :-) – cfischer Nov 26 '14 at 16:28
3

As a convenience, I came up with this workaround so that I don't have to use the longer initializer everywhere. I have a BaseViewController that all my controllers extend.

init () {
    let className = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last
    super.init(nibName: className, bundle: NSBundle(forClass: self.dynamicType))
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

Note, using NSBundle(forClass: self.dynamicType) takes care of namespacing in different targets.

Then when I want to instantiate a ViewController, I use MyViewController() as you could in Objective-C.

Mike Cole
  • 1,291
  • 11
  • 19
0
let Vc = UIViewController()
vc.view = yourview

or add above vc to View as ChildviewController

configureChildViewController(vc,onView:yourview in Viewcontroller)

Use this Extension for UiViewController

extension UIViewController {


func configureChildViewController(childController: UIViewController, onView: UIView?) {
    var holderView = self.view
    if let onView = onView {
        holderView = onView
    }
    addChildViewController(childController)
    holderView.addSubview(childController.view)
    constrainViewEqual(holderView, view: childController.view)
    childController.didMoveToParentViewController(self)
}

func scatterChildViewController(childController: UIViewController) {
    childController.willMoveToParentViewController(self)
    childController.view.removeFromSuperview()
    childController.removeFromParentViewController()
}

func constrainViewEqual(holderView: UIView, view: UIView) {
    view.translatesAutoresizingMaskIntoConstraints = false
    //pin 100 points from the top of the super
    let pinTop = NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal,
        toItem: holderView, attribute: .Top, multiplier: 1.0, constant: 0)
    let pinBottom = NSLayoutConstraint(item: view, attribute: .Bottom, relatedBy: .Equal,
        toItem: holderView, attribute: .Bottom, multiplier: 1.0, constant: 0)
    let pinLeft = NSLayoutConstraint(item: view, attribute: .Left, relatedBy: .Equal,
        toItem: holderView, attribute: .Left, multiplier: 1.0, constant: 0)
    let pinRight = NSLayoutConstraint(item: view, attribute: .Right, relatedBy: .Equal,
        toItem: holderView, attribute: .Right, multiplier: 1.0, constant: 0)

    holderView.addConstraints([pinTop, pinBottom, pinLeft, pinRight])
}

}

Phani Sai
  • 1,215
  • 19
  • 33