Update SnapKit Constraint offset

jalone picture jalone · Feb 3, 2017 · Viewed 15.7k times · Source

I am using SnapKit and can't find a clean way to update the offset of a constraint. It's a simple loading bar view whose inner view (expiredTime) has to fill the parent (left to right) according to percentage.

I create the constraint in the awakeFromNib of a custom UIView

    self.expiredTime.snp.makeConstraints { (make) in
        make.left.equalTo(self)
        make.top.equalTo(self)
        make.bottom.equalTo(self)
        self.constraintWidth = make.width.equalTo(self).constraint 
    }
    setNeedsUpdateConstraints()

then whenever the time is updated i call setNeedsUpdateConstraints() which will trigger the default updateConstraints() which has been overloaded as by apple hint:

Does not work: (the expiredTime view always fit with the parent view)

override func updateConstraints() {

    let offset = self.frame.width * CGFloat(percentage)

    self.expiredTime.snp.updateConstraints { (make) in
        make.width.equalTo(self).offset(offset).constraint
    }
    super.updateConstraints()
}

This also does not work:

override func updateConstraints() {
    let offset = self.frame.width * CGFloat(percentage)
    self.constraintWidth?.update(offset: offset)
    super.updateConstraints()
}

Rebuilding all the constraint works but i would like to avoid it

override func updateConstraints() {

    self.expiredTime.snp.remakeConstraints() { (make) in
        make.left.equalTo(self)
        make.top.equalTo(self)
        make.bottom.equalTo(self)
        self.constraintWidth = make.width.equalTo(self).multipliedBy(self.percentage).constraint
    }
    super.updateConstraints()
}

Answer

joern picture joern · Feb 13, 2017

Your first solution does not work, because you already set the width of your expiredTime view to the full width before adding the offset. To make it work you have to set the width to 0 and then add the offset. But the offset is not really needed here, you can simply set the width to the calculated width:

override func updateConstraints() {
    let width = self.frame.width * CGFloat(percentage)
    self.expiredTime.snp.updateConstraints { (make) in
        make.width.equalTo(width)
    }
    super.updateConstraints()
}

Or if you keep a reference to the constraint you don't have to override updateConstraints() at all. You can simply call the constraint's update method (without calling setNeedsUpdateConstraints() afterwards)

constraintWidth?.update(offset: CGFloat(percentage) * view.bounds.width)

Just remember to set the width to 0 when you initialize constraintWidth:

self.expiredTime.snp.makeConstraints { (make) in
    make.left.top.bottom.equalTo(self)
    self.constraintWidth = make.width.equalTo(0).constraint 
}