How to create a custom initializer for a subclass of UIViewController in Swift?

Sorry if this was asked before, I searched a lot many times, and many answers came from Swift's earlier blinds, when things were different. I can not find the final answer.

I want to subclass UIViewController and have a custom initializer so that I can easily configure it in code. I have problems with this in Swift.

I need an init() function that I can use to pass a specific NSURL , which I will use with the view controller. In my opinion, it looks like init(withImageURL: NSURL) . If I add this function, it will ask me to add the init(coder: NSCoder) function init(coder: NSCoder) .

I believe this is due to the fact that it was marked in the superclass with the keyword required ? So should I do this in a subclass? I add it:

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

Now what? My special initializer is considered a convenience one? Appointed? Can I call a super initializer? An initializer from the same class?

How to add my special initializer to a subclass of UIViewController ?

+74
initialization uiviewcontroller swift
Nov 14 '14 at 4:40
source share
5 answers
 class ViewController: UIViewController { var imageURL: NSURL? // this is a convenient way to create this view controller without a imageURL convenience init() { self.init(imageURL: nil) } init(imageURL: NSURL?) { self.imageURL = imageURL super.init(nibName: nil, bundle: nil) } // if this view controller is loaded from a storyboard, imageURL will be nil /* Xcode 6 required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } */ // Xcode 7 & 8 required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } } 
+130
Nov 14 '14 at 5:16
source share

For those who write an interface in code

 class Your_ViewController : UIViewController { let your_property : String init(your_property: String) { self.your_property = your_property super.init(nibName: nil, bundle: nil) } override func viewDidLoad() { super.viewDidLoad() } required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") } } 
+6
Mar 17 '19 at 14:53
source share

Convenient initializers are secondary, supporting initializers for the class. You can define a convenient initializer to call the assigned initializer from the same class as the convenient initializer, with some of the assigned initializer parameters set to default values. You can also define a convenient initializer to instantiate this class for a specific use case or type of input value.

They are documented here .

+5
Nov 14 '14 at 4:59
source share

This is very similar to the other answers, but with some explanation. The accepted answer is misleading because its property is optional and does not disclose the fact that your init?(coder: NSCoder) SHOULD initialize each property, and the only solution that has fatalError() . This means that you can do without additional properties, but this does not give an exact answer to the OP question.

 // Think more of a OnlyNibOrProgrammatic_NOTStoryboardViewController class ViewController: UIViewController { let name: String override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .blue } // I don't have a nib. It all through my code. init(name: String) { self.name = name super.init(nibName: nil, bundle: nil) } // I have a nib. I'd like to use my nib and also initialze the 'name' property init(name: String, nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle? ) { self.name = name super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } // when you do storyboard.instantiateViewController(withIdentifier: "ViewController") // The SYSTEM will never call this! // it wants to call the required initializer! init?(name: String, coder aDecoder: NSCoder) { self.name = "name" super.init(coder: aDecoder) } // when you do storyboard.instantiateViewController(withIdentifier: "ViewController") // The SYSTEM WILL call this! // because this is its required initializer! // but what are you going to do for your 'name' property?! // are you just going to do 'self.name = "default Name" just to make it compile?! // Since you can't do anything then it just best to leave it as 'fatalError()' required init?(coder aDecoder: NSCoder) { fatalError("I WILL NEVER instantiate through storyboard! It impossible to initialize super.init?(coder aDecoder: NSCoder) with any other parameter") } } 

You basically have to refuse to download it from the storyboard. Why?

Because when you call viewController storyboard.instantiateViewController(withIdentifier: "viewController") , the SYSTEM will do its job and call

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

You can never redirect this call to another init method. However, for programmatically created viewController or nib created viewControllers, you can redirect this call as shown above.

+1
Sep 19 '19 at 19:34
source share

If you need a custom init for popover, for example, you can use the following approach:

Create a custom init that uses super init with nibName and bundle, and then access the view property to force the view hierarchy to load.

Then, in the viewDidLoad function, you can configure the views with the parameters passed during initialization.

 import UIKit struct Player { let name: String let age: Int } class VC: UIViewController { @IBOutlet weak var playerName: UILabel! let player: Player init(player: Player) { self.player = player super.init(nibName: "VC", bundle: Bundle.main) if let view = view, view.isHidden {} } override func viewDidLoad() { super.viewDidLoad() configure() } func configure() { playerName.text = player.name + "\(player.age)" } } func showPlayerVC() { let foo = Player(name: "bar", age: 666) let vc = VC(player: foo) present(vc, animated: true, completion: nil) } 
0
Feb 05 '18 at 9:49
source share



All Articles