How do I make a custom initializer for a UIViewController subclass in Swift?
Solution 1
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
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Solution 2
For those who write UI 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")
}
}
Solution 3
This is very similar to the other answers, but with some explanation. The accepted answer is misleading because its property is optional and doesn't expose the fact that your init?(coder: NSCoder)
MUST initialize each and every property and the only solution to that is having a fatalError()
. Ultimately you could get away by making your properties optionals, but that doesn't truly answer the OP’s question.
// Think more of a OnlyNibOrProgrammatic_NOTStoryboardViewController
class ViewController: UIViewController {
let name: String
override func viewDidLoad() {
super.viewDidLoad()
}
// I don't have a nib. It's 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's just best to leave it as `fatalError()`
required init?(coder aDecoder: NSCoder) {
fatalError("I WILL NEVER instantiate through storyboard! It's impossible to initialize super.init?(coder aDecoder: NSCoder) with any other parameter")
}
}
You basically have to ABANDON loading it from storyboard. Why?
Because when you call a viewController storyboard.instantiateViewController(withIdentifier: "viewController")
then UIKit will do its thing and call
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
You can never redirect that call to another init method.
Docs on
instantiateViewController(withIdentifier:)
:Use this method to create a view controller object to present programmatically. Each time you call this method, it creates a new instance of the view controller using the
init(coder:)
method.
Yet for programmatically created viewController or nib created viewControllers you can redirect that call as shown above.
Solution 4
Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.
They are documented here.
Related videos on Youtube
Doug Smith
I'm a web designer playing around with iOS from England
Updated on August 16, 2021Comments
-
Doug Smith over 2 years
Apologies if this has been asked before, I've searched around a lot and many answers are from earlier Swift betas when things were different. I can't seem to find a definitive answer.
I want to subclass
UIViewController
and have a custom initializer to allow me to set it up in code easily. I'm having trouble doing this in Swift.I want an
init()
function that I can use to pass a specificNSURL
I'll then use with the view controller. In my mind it looks something likeinit(withImageURL: NSURL)
. If I add that function it then asks me to add theinit(coder: NSCoder)
function.I believe this is because it's marked in the superclass with the
required
keyword? So I have to do it in the subclass? I add it:required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
Now what? Is my special initializer considered a
convenience
one? A designated one? Do I call a super initializer? An initializer from the same class?How do I add my special initializer onto a
UIViewController
subclass?-
mfaani over 4 yearsInitializers are good for programmatically created viewControllers, but for viewcontrollers created through storyboard you're out of luck and have to work your way around it
-
-
Van Du Tran about 9 yearscan imageURL be a constant instead? ("let imageURL: NSURL?)
-
linimin about 9 yearsSure @VanDuTran, we can initiailze a constant in initiailzers.
-
siege_Perilous almost 9 yearsYou don't need to call any designated initializers in your new default initializer?
-
Chris Hayes about 8 yearsnot working xCode 7, the required constructor is failing to initialize the class level property, in my case an Int, not NSURL?
-
Nikola Lukic over 7 yearsHow to load this kind of viewController ?
-
linimin over 7 years@NikolaLukic - We can creates a new instance of the class by calling
ViewController()
,ViewController(imageURL: url)
, or loading it from a storyboard. -
Nikola Lukic over 7 yearsNow i get it , i can make manipulation with class instance before i put it in the present view ! Nice...
-
Sujay U N about 7 yearsI get this error Convenience initializer for 'NamCls' must delegate (with 'self.init') rather than chaining to a superclass initializer (with 'super.init')
-
linimin about 7 years@SujayUN - A convenience initializer can only call another initializer from the same class.
-
mfaani over 6 yearsI had to write mine as
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
otherwise withsuper.init(coder: aDecoder)
I was getting the error of Propertyself.someProperty
not initialized atsuper.init
call -
linimin over 6 years@Honey - All stored property must be assigned an initial value before calling
super.init
. In the sample code above,imageURL
is an optional variable, so it's nil by default. -
mfaani over 6 yearsNot sure how that's related. If you're doing pure code, then you don't need to populate your properties inside the
init(coder: aDecoder)
. ThefatalError
will suffice -
linimin over 6 years@Honey - Yes, that's totally fine.
-
mfaani over 4 years@others This answer is slightly misleading. I put a different answer here which addresses the comments I made before.
-
Peter Kovacs over 2 yearsThis isn't strictly true any more, you can "redirect" the call to another init method by using the
instantiateViewController(identifier:creator:)
method that's available in iOS 13+