Search code examples
iosswiftnslayoutconstraint

NSLayoutConstraint not updating every time


I was testing with NSLayoutConstraint and found that layouts are not updating every time.
Here is my ViewController's IB. Took constraints in IB Here I've taken IBAction for valueChange of Stepper

@IBAction func handleStepperValueChanged(_ sender: UIStepper) {
    progressBar.progress = Float(sender.value / sender.maximumValue)
    lblProgress.text = String(format: "%.2f %", progressBar.progress)

    updateMyConstraint()
}
func updateMyConstraint() {
    for constraints in lblProgress.constraints {
        if constraints.firstAttribute == .width {
            lblProgress.removeConstraint(constraints)
        }
    }
    let multiplier = CGFloat(progressBar.progress)
    let constLblWidth = NSLayoutConstraint(item: lblProgress, attribute: .width, relatedBy: .equal, toItem: progressBar, attribute: .width, multiplier: multiplier, constant: 0.0)
    constLblWidth.priority = .defaultHigh
    lblProgress.translatesAutoresizingMaskIntoConstraints = false
    constLblWidth.isActive = true
    lblProgress.updateConstraints()
}

and here is my output. (which is not updating each time when I update Stepper's value) 0.40 0.50 0.60

Is there something that I'm missing to update layouts? Please help me to solve this.
Each time update ProgressBar label also should increase its width.

I've also tried to update layout via below statements.

self.view.layoutIfNeeded()
self.view.layoutSubviews()

Solution

  • The problem is here

    lblProgress.removeConstraint(constraints)
    

    the constraint should be removed from the parent of both lblProgress and progressBar because the constraint is between them

      func updateMyConstraint() {
        for constraints in self.view.constraints {
    
            if constraints.identifier == "someIdentifier"  && constraints.firstAttribute == .width {
                  print(constraints)
                  self.view.removeConstraint(constraints)
            }
        }
        let multiplier = CGFloat(progressBar.progress)
        let constLblWidth = NSLayoutConstraint(item: lbl, attribute: .width, relatedBy: .equal, toItem: progressBar, attribute: .width, multiplier: multiplier, constant: 0.0)
        constLblWidth.priority = .required
        lbl.translatesAutoresizingMaskIntoConstraints = false
        constLblWidth.isActive = true
        constLblWidth.identifier = "someIdentifier"
        self.view.layoutIfNeeded()
    }
    

    constLblWidth.isActive = true is originally add the constraint to the shared parent between the 2 items which is self.view in this case , so when you remove the constraint you should remove it from self.view you can verify that by printing the constraints here is a run with my edit

    enter image description here