Search code examples
iosswift3nslayoutconstraint

@IBDesignable , NSLayoutConstraint for proportional multiplier width of superview?


In an @IBDesignable,

I'm trying to programmatically set "width 20% of parent":

@IBDesignable
class TwentyPercentExample:UIView {

    func setup() {
        let cWidth = NSLayoutConstraint(
            item: self,
            attribute: NSLayoutAttribute.width,
            relatedBy: NSLayoutRelation.equal,
            toItem: self.superview,
            attribute: NSLayoutAttribute.width,
            multiplier: 0.2,
            constant:0
        )
        addConstraint(cWidth)
        print("I seemed to added the width constraint....")
        updateConstraintsIfNeeded() // could be useful..
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setup()
    }

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

}

(So, you'd add a UIView in storyboard, perhaps set it anchored on the left to the superview, and then change the class to TwentyPercentExample.)

Strangely this doesn't work. if you do this:

multiplier: 1,
constant:100

it nicely sets it, in realtime, in storyboard to 100 width. Change to

multiplier: 1,
constant:200

and it works fine, changes it in realtime to 200 width. However this just doesn't seem to work:

multiplier: 0.2,
constant:0

Do I have toItem: wrong, or something? What's the deal?


Solution

  • I suspect the problem is that you're doing this in init when self.superview is nil. You should wait to add the constraint until after its been added to the superview. Perhaps in didMoveToSuperview(), though this could get messy since you'll need to account for the fact that it could be added to a superview more than once.

    Probably the reason the fixed constant case works is because its legal to have a constraint that's hardcoded to 100 with a nil item as the toItem: argument.


    So, either of these

    override func didMoveToSuperview() { setup() }
    ... or ...
    override func layoutSubviews() { setup() }
    
    func setup() {
        self.widthAnchor
          .constraint(equalTo: superview!.widthAnchor, multiplier: 0.2)
          .isActive = true
    }
    

    seem to work: but seems to work irregularly and generate "agent crashed" errors in Xcode.