Unable to programatically create a UIViewController in Swift

11.2k views Asked by At

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????

3

There are 3 answers

10
Lyndsey Scott On

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)
0
Phani Sai On
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])
}

}

0
Mike Cole On

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.