18
votes
let view = UIView()

is acceptable, but the only UIView initializer documented is init(frame: CGRect)

specifically, I am trying to write a new class that inherits from UIView, but this code throws an error

class SquadHorizontalScrollViewCell: UIView {

    init(var: FIRDataSnapshot){
        super.init()
....

says must call designated initializer

5

5 Answers

4
votes

UIView inherits from UIResponder which inherits from NSObject.

The NSObject.init() initializer is accessible on UIView, but not on subclasses of NSObject which replace this designated initializer.

Let's consider an example.

class A: NSObject { 
    init(_ string: String) { } 
}

This leads to a compiler error for let a = A() - missing argument #1 for initializer because this initializer replaces the designated init() initializer for NSObject in the subclass.

You just need to define the initializer of the subclass as a convenience initializer:

class A: NSObject { 
    convenience init(_ string: String) { 
        self.init() // Convenience initializers must call a designated initializer.
    } 
}

let a = A() now compiles.

UIView can also compile with other designated initializers defined in the subclass, since it's designated initializer is not known at compile time. As per the docs, if instantiating programmatically, init(frame:) is the designated initializer, otherwise init() is the designated initializer. This means that UIView inherits the NSObject designated initializer rather than replacing it as in the above example.

In your example:

class SquadHorizontalScrollViewCell: UIView {

    init(var: FIRDataSnapshot){
        super.init()

We see that the designated initializer is init(frame: CGRect), so you have to call this designated initializer instead.

2
votes

A designated initializer should call its superclass designated initializer.

In this case super.init() is the designated initializer of NSObject not UIView. It would be UIView's responsibility to call UIResponder init ,I guess it has no designated initializer, hence UIView will call Super.init in its init(frame:CGrect) initializer. check "Initializer Delegation"

for why let x = UIView() is ok , its because of this

Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default. Swift’s approach prevents a situation in which a simple initializer from a superclass is inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized. (Apple)

since UIView is objective c class it still can do it. but you won't be able to call SquadHorizontalScrollViewCell() unless you did not provide any initializer or you overrides the designated initializer of the superclass (UIView)


Check this link for more info

1
votes

For UIView init(frame: CGRect) is default initializer. You must call it after initialize your instance variable. If you take view from NIB then init?(coder aDecoder: NSCoder) is called instead of init(frame: CGRect). So in that case you have to initialize your instance variable in awakeFromNib() method. In this case your code should be like this:

class SquadHorizontalScrollViewCell: UIView {

    init(firDataSnapshot: FIRDataSnapshot){

        // intialize your instance variable
        super.init(frame: CGRectZero) // set your expected frame. For demo I have set `CGRectZero`

    }

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

}

For more info you can check this link https://developer.apple.com/reference/uikit/uiview

1
votes

At a certain point your view will need to be init with something, that is why the compilation is complaining, because it cannot find how to start the initialisation of your custom view. Because at the end, a view will be init from a xib (init(coder aDecoder: NSCoder)), or from a frame ( init(frame: CGFrame)). So here, the easiest way is to call super.init(frame: CGRectZero) at least in your custom init method.

init (var: FIRDataSnapshot) {
        super.init(frame: CGRectZero)   
}
// This method below is always needed when you want to override your init method
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

but you'll still need to set the size of your frame etc.

0
votes

You'll notice if you create your own UIView subclass and only override init(frame:) with a log statement, then instantiate this new class using just init(), your init(frame:) is actually called with a zero-sized frame. So the designated initializer is still getting called.