Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Update Constraint

I added the constraint to the buttons created in my UIView

func CreateButtonWithIndex(index:Int) {

    newButton.setTranslatesAutoresizingMaskIntoConstraints(false)

    self.view.addSubview(newButton)

    let newButtonConstraintL = NSLayoutConstraint(item: newButton, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 50)
    let newButtonConstraintH = NSLayoutConstraint(item: newButton, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 50)
    var newButtonConstraintX = NSLayoutConstraint(item: newButton, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: CGFloat(riga))
    var newButtonConstraintY = NSLayoutConstraint(item: newButton, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: CGFloat(colonna))

    view.addConstraints([newButtonConstraintX,newButtonConstraintY,newButtonConstraintH,newButtonConstraintL])

    var pan = UIPanGestureRecognizer(target:self, action:"pan:")
    pan.maximumNumberOfTouches = 2
    pan.minimumNumberOfTouches = 1
    newButton.addGestureRecognizer(pan)
}

func pan(rec:UIPanGestureRecognizer) {
  case .Ended: 
        if let subview = selectedView {
            if let button = rec.view as? UIButton {
                if let title = button.titleForState(.Normal){
                    button.setTranslatesAutoresizingMaskIntoConstraints(false)
                    let line = Float(p.x % snapX)
                    let column = Float(p.x % snapY)
                    let constraintL = NSLayoutConstraint(item: button, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 50)
                    let constraintH = NSLayoutConstraint(item: button, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 50)
                    var constraint2 = NSLayoutConstraint(item: button, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: CGFloat(line))
                    var constraint3 = NSLayoutConstraint(item: button, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: CGFloat(column))

                    view.addConstraints([constraint2,constraint3,constraintL.constraintH])
                }
            }
        }
}

In My Project my users can move these buttons and then I tried to add more constraints to the buttons recognized but I just get an endless error constraint

  "<NSLayoutConstraint:0x7f8e5a4e9450 UIButton:0x7f8e5a644f60'Button6'.centerX == UIView:0x7f8e5a648bc0.centerX - 120>",
  "<NSLayoutConstraint:0x7f8e5a5be040 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7f8e5a648bc0(736)]>",
  "<NSAutoresizingMaskLayoutConstraint:0x7f8e5a5be1f0 h=-&- v=-&- 'UIView-Encapsulated-Layout-Left' H:|-(0)-[UIView:0x7f8e5a648bc0]   (Names: '|':UITransitionView:0x7f8e5a5a8190 )>",
  "<NSAutoresizingMaskLayoutConstraint:0x7f8e5a5b53b0 h=--& v=--& UIButton:0x7f8e5a644f60'Button6'.midX == + 200>"

Will attempt to recover by breaking constraint

"<NSLayoutConstraint:0x7f8e5a4e9450 UIButton:0x7f8e5a644f60'Button6'.centerX == UIView:0x7f8e5a648bc0.centerX - 120>"

"<NSLayoutConstraint:0x7f8e5a4e9510 UIButton:0x7f8e5a644f60'Button6'.centerY == UIView:0x7f8e5a648bc0.centerY - 40>",
"<NSLayoutConstraint:0x7f8e5a5be1a0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7f8e5a648bc0(414)]>",
"<NSAutoresizingMaskLayoutConstraint:0x7f8e5a5be240 h=-&- v=-&- 'UIView-Encapsulated-Layout-Top' V:|-(0)-[UIView:0x7f8e5a648bc0]   (Names: '|':UITransitionView:0x7f8e5a5a8190 )>",
"<NSAutoresizingMaskLayoutConstraint:0x7f8e5a5b82d0 h=--& v=--& UIButton:0x7f8e5a644f60'Button6'.midY == + 189>"

Will attempt to recover by breaking constraint

<NSLayoutConstraint:0x7f8e5a4e9510 UIButton:0x7f8e5a644f60'Button6'.centerY == UIView:0x7f8e5a648bc0.centerY - 40>

... should I update newButtonConstraintX / Y but I'm not understanding how I can do this ?

like image 756
stack Avatar asked Jan 09 '15 21:01

stack


People also ask

How do I update constraints in programmatically in Swift?

Select the height constraint from the Interface builder and take an outlet of it. So, when you want to change the height of the view you can use the below code. Method updateConstraints() is an instance method of UIView . It is helpful when you are setting the constraints programmatically.

How do I change the constraint multiplier in Swift?

Since multiplier is a read-only property and you can't change it, you need to replace the constraint with its modified clone.

When should I call updateConstraints?

When a property of your custom view changes in a way that would impact constraints, you can call this method to indicate that the constraints need to be updated at some point in the future. The system will then call updateConstraints() as part of its normal layout pass.


1 Answers

The issue is that you're adding a new constraint that conflicts with the existing constraint.

You have a few options:

  1. Effective in iOS 8, you can set the active property to false for a constraint before you add a new constraint.

  2. In iOS versions prior to 8, you would want to remove the old constraints before adding new constraints.

  3. Ideally, it's best to not have to activate/deactivate (or, worse, add and remove) constraints, but rather just modify the constant property of a single constraint. For example in Swift 3/4:

    class ViewController: UIViewController {
    
        private var xConstraint: NSLayoutConstraint!
        private var yConstraint: NSLayoutConstraint!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let label = UILabel()
            label.text = "x"
            label.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(label)
    
            // I don't really need to save references to these, so these are local variables
    
            let widthConstraint = label.widthAnchor.constraint(equalToConstant: 50)
            let heightConstraint = label.heightAnchor.constraint(equalToConstant: 50)
    
            // but since I'll be modifying these later, these are class properties
    
            xConstraint = label.centerXAnchor.constraint(equalTo: view.centerXAnchor)
            yConstraint = label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
    
            NSLayoutConstraint.activate([widthConstraint, heightConstraint, xConstraint, yConstraint])
    
            let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
            view.addGestureRecognizer(pan)
        }
    
        private var originalCenter: CGPoint!
    
        @objc func handlePan(_ gesture: UIPanGestureRecognizer) {
            if gesture.state == .began {
                originalCenter = CGPoint(x: xConstraint.constant, y: yConstraint.constant)
            }
    
            let translation = gesture.translation(in: gesture.view!)
    
            xConstraint.constant = originalCenter.x + translation.x
            yConstraint.constant = originalCenter.y + translation.y
        }
    
    }
    

    When the desired effect can be achieved by modifying the constant of the constraint, that's generally best.

For Swift 2 syntax, see previous revision of this answer.

like image 123
Rob Avatar answered Oct 26 '22 23:10

Rob