Igor Pantović
Igor Pantović

Reputation: 9246

UIView init override causes IBDesignable to crash

I have a very strange problem with XCode 7.1 interface builder. I have a really simple UIView subclass, which renders fine in storyboard editor:

import UIKit

@IBDesignable
class DashboardHeaderView: UIView {

    @IBInspectable
    var maskClipHeight: CGFloat = 40.0

    override func layoutSubviews() {
        super.layoutSubviews()
        self.setMask()
    }

    private func setMask() {
        let mask = CAShapeLayer()
        mask.path = self.createMaskPath()
        self.layer.mask = mask
    }

    private func createMaskPath() -> CGPath {
        let maskPath = UIBezierPath()
        maskPath.moveToPoint(CGPoint(x: bounds.minX, y: bounds.minY))
        maskPath.addLineToPoint(CGPoint(x: bounds.maxX, y: bounds.minY))
        maskPath.addLineToPoint(CGPoint(x: bounds.maxX, y: bounds.maxY - self.maskClipHeight))
        maskPath.addLineToPoint(CGPoint(x: bounds.minX, y: bounds.maxY))
        maskPath.closePath()

        return maskPath.CGPath
    }

}

However, if I only add initializer override to it:

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

It fails with errors:

I am 100% certain that that initializer override makes it crash as I've reproduced it a couple of times. If I only comment it out, it works again.

Anyone has any idea why this is happening and if there is a way to fix/workaround it?

Upvotes: 4

Views: 1643

Answers (3)

Robert Schmid
Robert Schmid

Reputation: 454

I've been struggling with this all day. You need to implement:

override init(frame: frame) {
    super.init(frame: frame)
}

That's the initializer the IBDesignable agent uses to instantiate the class. So, in my case I had another initializer as well.

init(frame: CGRect, maxValue: Double, minValue: Double) {
    super.init(frame: frame)

    self.maxValue = maxValue
    self.minValue = minValue
}

My init was blocking the init that IBDesignable needs. Once I overrode the default init as above I had the choice of leaving my init as is or converting it to a convenience init:

convenience init(frame: CGRect, maxValue: Double, minValue: Double) {
    self.init(frame: frame)

    self.maxValue = maxValue
    self.minValue = minValue
}

Now I can add some default behavior for IBDesigner:

var initForIB = false

init(frame: CGRect, maxValue: Double, minValue: Double) {
    super.init(frame: frame)

    self.maxValue = maxValue
    self.minValue = minValue
    initForIB = false
}

override init(frame: CGRect) {
    super.init(frame: frame)
    initForIB = true
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func drawRect(rect: CGRect) {
    if initForIB {
        initIBDefaults()
    }
    // ...do some other stuff...
}

Upvotes: 2

dtarnok
dtarnok

Reputation: 151

I've found the answer on another SO page: @IBDesignable crashing agent

You need to override both init(frame:) and init?(coder:). If you override only one of the two, IB rendering will crash.

Upvotes: 1

Marco M
Marco M

Reputation: 1235

I've had a similar problem and found that setting a mask for ibdesignable views outside of prepareForInterfaceBuilder() function cause a rendering crash... prepareForInterfaceBuilder() is not called from the system, only by interfaceBuilder, so you will need to set the maskView here and in awakeFromNib().

Upvotes: 0

Related Questions