How to write init methods of a UIViewController in Swift
Solution 1:
I used:
convenience init() {
self.init(nibName:nil, bundle:nil)
}
Some people suggested:
convenience init(){
self.init()
}
But this gives you an infinite loop.
Solution 2:
All of these answers are half-answers. Some people are experiencing infinite loops in related posts because they are only adding the convenience init()
(it recursively calls itself if you don't providing a distinct init()
method for it to invoke), while other people are neglecting to extend the superclass. This format combines all the solutions to satisfy the problem completely.
// This allows you to initialise your custom UIViewController without a nib or bundle.
convenience init() {
self.init(nibName:nil, bundle:nil)
}
// This extends the superclass.
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
}
// This is also necessary when extending the superclass.
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") // or see Roman Sausarnes's answer
}
Edit: Also, if you want to initialise any class properties using parameters passed into your convenience init()
method without all the overridden init()
methods complaining, then you may set all those properties as implicitly unwrapped optionals, which is a pattern used by Apple themselves.
Solution 3:
You need to override the init(nibName:bundle:)
initializer, and provide the init(coder:)
required initializer, and initialize tap
in both of them:
class ViewController: UIViewController {
var tap: UITapGestureRecognizer?
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
print("init nibName style")
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
}
// note slightly new syntax for 2017
required init?(coder aDecoder: NSCoder) {
print("init coder style")
super.init(coder: aDecoder)
tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
}
...
...
}
Also, be sure when calling the superclass initializer that you don't just pass nil
for the nibName
and bundle
. You should pass up whatever was passed in.
Note that the "convenience init" method mentioned in some answers is only relevant if you are actually "by hand" yourself initializing a view controller. (There are very few situations where you would do that.) If you want to "do something during normal initialization" - so for example create a gesture recognizer or just initialize some variables - the only possible solution is exactly the one given in this answer.
Solution 4:
You can just create a convenience initializer instead, convenience initializers don't override the init() instead it just calls it making things easier.
class ViewController: UIViewController {
var tap: UITapGestureRecognizer?
convenience init(){
self.init()
tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
}
}
Solution 5:
I think you have to add
required init(coder aDecoder: NSCoder) {
print("init coder")
super.init(coder: aDecoder)
}
or
convenience init() {
self.init()
}
You have to write init too. Don't remove it
var tap: UITapGestureRecognizer?
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nil, bundle: nil)
}
Hope it helps