Cannot subclass UIButton: Must call a designated initializer of the superclass 'UIButton' - ios

Trying to subclass UIButton but the error Must call a designated initializer of the superclass 'UIButton' occurs.
Researching several SO posts like this, this, this, or several others did not help as those solutions didn't work.
How can we subclass UIButton in Swift and define a custom init function?
import UIKit
class KeyboardButton : UIButton {
var letter = ""
var viewController:CustomViewController?
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(letter: String, viewController: CustomViewController) {
super.init()
...
}
}

You have to call the superclass' designated initializer:
Swift 3 & 4:
init(letter: String, viewController: CustomViewController) {
super.init(frame: .zero)
}
Swift 1 & 2:
init(letter: String, viewController: CustomViewController) {
super.init(frame: CGRectZero)
}
As Paulw11 says in the comments, a view generally shouldn't have a reference to its controller, except as a weak reference using the delegate pattern, which would promote reusability.

Related

Forcing Nib loader use init?(with:) for custom subclass of NSObject in iOS

I add to a XIB custom NSObject, set its class to my subclass of NSObject that conforms NSCoding protocol. Through initialisation UINibLoader use init().
According to Appleā€™s Resource Programming Guide in iOS any object that conform NSCoding protocol will be send -initWithCoder: message.
Is it possible, and if it is how to force UIKit to use init?(with:) for custom subclass of NSObject?
UPD: My class:
class SomeClass: NSObject, NSCoding {
static var sharedSomeClass: SomeClass = {
return SomeClass()
}()
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
}
override func encode(with aCoder: NSCoder) {
super.encode(with: aCoder)
}
override func awakeAfter(using aDecoder: NSCoder) -> Any? {
return SomeClassManager.sharedSomeClass
}
}
In XIB I connect to #IBOutlet var someObject: SomeClass! in ViewController.
Try this:
To force it to use another init method, add the -initWithCoder method to your subclass and override it to call your own init method like so:
Objective-C: (someone add translation to Swift for me please)
-(instancetype)initWithCoder(NSCoder*)aDecoder{
self = [super initWithCoder:aDecoder];
if(self){
[self customInit];
}
return self;
}
That way when -initWithCoder is called, your init method will end up being called. Do the same for -initWithFrame

Swift 2.0 Subclassing a subclass of UIViewController and called convenience initializers

Have a bit of confusion regarding designated and convenience initializers for UIViewController in Swift 2.0/Xcode 7beta3. Our UIViewControllers are all defined in code, there are no Nibs
Currently class A inherits from UIViewController like this
class A : UIViewController {
convenience init() {
...
self.init(nibName:nil, bundle:nil)
...
}
}
Then class B inherits from class A and should override the convenience init and call its as super.init()
class B : A {
convenience init() {
super.init()
...
}
}
The compiler does not allow this with Must call a designated initializer of the superclass '...' error on super.init()
You need to make your initializers designated, not convenience:
class A : UIViewController {
init() {
super.init(nibName:nil, bundle:nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("")
}
}
class B : A {
override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("")
}
}
That gives you the inheritance structure you're looking for.
Take a look at these images found in the documentation.
(source: apple.com)
(source: apple.com)
According to the image convenience initializers are not inherited. So if you want to inherit you must make it a designated initializer.
class A : UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
class B : A {
override init() {
super.init()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
At this Point you may wonder what the heck is the difference between a designated and convenience initializer? Well, Convenience is used to call a designated initializer in the same class and you are suppose to use this to do some set up.
According to the document
Rule 2 A convenience initializer must call another initializer from the same class.
From https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html(Initializer Delegation for Class Types)
You should call it's own initializer instead:
class B : A {
convenience init() {
init()
...
}
}
And init() is automatically inherit from it's superclass version
Here are rules how communicating designated initializers and convenience initializers
rule 1:
A designated initializer must call a designated initializer from its immediate superclass.
Rule 2:
A convenience initializer must call another initializer from the same class.
Rule 3:
A convenience initializer must ultimately call a designated initializer.
A simple way to remember this is:
Designated initializers must always delegate up.
Convenience initializers must always delegate across.

Swift 1.2: override init gives error [duplicate]

So I've just upgraded to Xcode 6.3 Beta 3 and a lot of error(s) are appearing relating to the following:
Initializer does not override a designated initializer from its superclass.
override init() {
super.init()
}
For example this is a UIButton class:
class CustomButton: UIButton {
var target: AnyObject!
var selector: Selector!
var action: (() -> Void)!
override init() { // Initializer does not override a designated initializer from its superclass
super.init() // Must call a designated initializer of the superclass 'UIButton'
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
}
This is one of my UIViewController classes:
class CustomAlertView: UIViewController {
required init(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
required override init() { // Initializer does not override a designated initializer from its superclass
super.init() // Must call a designated initializer of the superclass 'UIViewController'
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
My solution is a quick fix, but I think is easier than what Apple purposes on the the Release Notes. For more information search for 19775924 http://adcdownload.apple.com//Developer_Tools/Xcode_6.3_beta_3/Xcode_6.3_beta_3_Release_Notes.pdf here. What Apple says is that you create an Objective-C file and extend it (having to add it to the header files and all) and it's on "Known Issues in Xcode 6.3 beta 3", so I think is easy to do what I did:
This is how I fixed it for UIButton:
class CustomButton : UIButton {
init() {
super.init(frame: CGRectZero)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And this is one of my ViewControllers (remove public if not needed):
public class GenericViewController: UIViewController {
public init() {
super.init(nibName: nil, bundle: nil)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I don't use IB so I also have UIView, because I do separate the view from the viewController (remove public if not needed):
public class GenericMenuView: UIView {
public init() {
super.init(frame: CGRectZero)
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I need this specially in views because I have a setupViews method that I override in all subclasses that is called on the init. And using AutoLayout I don't need any frames (so I don't override the init with the frame parameter).
So it seems you have to drop override. Oh! and be sure to not call self.init() or the class is never initialized (and it crashes after some internal timeout).
As per Apple documentation here, what you are overriding is a convenience initializer. So for your initializer to work, you will have to change the method to
override convenience init() {
super.init()
}
You can either do that, or remove the initializer if you are not really using it except for calling the superclass initializer.
I recently figured this out and I'd like to explain what the problem was. Originally answered on the Apple Developer forums.
It seems Swift has changed the strategy for initializer dependency checking or for imporing initializers.
Now if your initializers' are as shown, one way to deal with both Xcode 6.3 Beta 2 and Beta 3 is to remove all initializer definitions:
class CustomButton: UIButton {
var target: AnyObject!
var selector: Selector!
var action: (() -> Void)!
}
class CustomAlertView: UIViewController {
}
Without defining any designated initializers, classes inherit all initializers of their superclasses.
A pretty easy fix, but a big gotcha that had me stumped for a while.
I think this is way easier than it seems.
For an SKSpriteNode, I was doing this:
override init() {
let texture = SKTexture(imageNamed: "bgTile")
super.init(texture: texture, color: nil, size: texture.size())
}
The problem is init() is not the designated initializer for SKSpriteNode. So I just changed it to:
override init(texture: SKTexture!, color: UIColor!, size: CGSize) {
let texture = SKTexture(imageNamed: "bgTile")
super.init(texture: texture, color: nil, size: texture.size())
}
Now it works fine.
Solution for Error : Override init(coder aDecoder: NSCoder!) not working like expected - Swift
This works for me , Try this, Note: u must awake nib
override func awakeFromNib() {
super.awakeFromNib()
// Initialisation code
}

Initializer does not override a designated initializer from its superclass

So I've just upgraded to Xcode 6.3 Beta 3 and a lot of error(s) are appearing relating to the following:
Initializer does not override a designated initializer from its superclass.
override init() {
super.init()
}
For example this is a UIButton class:
class CustomButton: UIButton {
var target: AnyObject!
var selector: Selector!
var action: (() -> Void)!
override init() { // Initializer does not override a designated initializer from its superclass
super.init() // Must call a designated initializer of the superclass 'UIButton'
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
}
This is one of my UIViewController classes:
class CustomAlertView: UIViewController {
required init(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
required override init() { // Initializer does not override a designated initializer from its superclass
super.init() // Must call a designated initializer of the superclass 'UIViewController'
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
My solution is a quick fix, but I think is easier than what Apple purposes on the the Release Notes. For more information search for 19775924 http://adcdownload.apple.com//Developer_Tools/Xcode_6.3_beta_3/Xcode_6.3_beta_3_Release_Notes.pdf here. What Apple says is that you create an Objective-C file and extend it (having to add it to the header files and all) and it's on "Known Issues in Xcode 6.3 beta 3", so I think is easy to do what I did:
This is how I fixed it for UIButton:
class CustomButton : UIButton {
init() {
super.init(frame: CGRectZero)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And this is one of my ViewControllers (remove public if not needed):
public class GenericViewController: UIViewController {
public init() {
super.init(nibName: nil, bundle: nil)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I don't use IB so I also have UIView, because I do separate the view from the viewController (remove public if not needed):
public class GenericMenuView: UIView {
public init() {
super.init(frame: CGRectZero)
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I need this specially in views because I have a setupViews method that I override in all subclasses that is called on the init. And using AutoLayout I don't need any frames (so I don't override the init with the frame parameter).
So it seems you have to drop override. Oh! and be sure to not call self.init() or the class is never initialized (and it crashes after some internal timeout).
As per Apple documentation here, what you are overriding is a convenience initializer. So for your initializer to work, you will have to change the method to
override convenience init() {
super.init()
}
You can either do that, or remove the initializer if you are not really using it except for calling the superclass initializer.
I recently figured this out and I'd like to explain what the problem was. Originally answered on the Apple Developer forums.
It seems Swift has changed the strategy for initializer dependency checking or for imporing initializers.
Now if your initializers' are as shown, one way to deal with both Xcode 6.3 Beta 2 and Beta 3 is to remove all initializer definitions:
class CustomButton: UIButton {
var target: AnyObject!
var selector: Selector!
var action: (() -> Void)!
}
class CustomAlertView: UIViewController {
}
Without defining any designated initializers, classes inherit all initializers of their superclasses.
A pretty easy fix, but a big gotcha that had me stumped for a while.
I think this is way easier than it seems.
For an SKSpriteNode, I was doing this:
override init() {
let texture = SKTexture(imageNamed: "bgTile")
super.init(texture: texture, color: nil, size: texture.size())
}
The problem is init() is not the designated initializer for SKSpriteNode. So I just changed it to:
override init(texture: SKTexture!, color: UIColor!, size: CGSize) {
let texture = SKTexture(imageNamed: "bgTile")
super.init(texture: texture, color: nil, size: texture.size())
}
Now it works fine.
Solution for Error : Override init(coder aDecoder: NSCoder!) not working like expected - Swift
This works for me , Try this, Note: u must awake nib
override func awakeFromNib() {
super.awakeFromNib()
// Initialisation code
}

UIView's designated initializers in swift

So I've subclassed UIView like so
class CustomView: UIView {
}
And I could do something like
let customView = CustomView()
But when I override what I believe are the two designated initialisers for UIView i.e. init(frame:) and init(coder:) like so
class CustomView: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.backgroundColor = UIColor.redColor()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.redColor()
}
}
I can no longer create the view like so as it complains that init() no longer exists
let customView = CustomView()
My understanding is that If I override all the designated initialisers, I should inherit all the convenience initializers as well.
So my question is. Is init() not a convenience initializer on UIView? Or is there another designated initializer on UIView that I haven't overrided yet?
init is not a convenience initializer on UIView. init is not an initializer on UIView at all! It is an initializer on its superclass NSObject - UIView merely inherits it. And it is a designated initializer on NSObject. So when you implemented a designated initializer, you cut off inheritance of designated initializers - including init.

Resources