web-dev-qa-db-fra.com

Contrainte de mise à jour rapide

J'ai ajouté la contrainte aux boutons créés dans ma 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])
                }
            }
        }
}

Dans Mon projet, mes utilisateurs peuvent déplacer ces boutons, puis j'ai essayé d'ajouter plus de contraintes aux boutons reconnus, mais j'ai juste une contrainte d'erreur sans fin. 

  "<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>"

Va tenter de récupérer en brisant la contrainte 

"<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>"

Va tenter de récupérer en brisant la contrainte 

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

... dois-je mettre à jour newButtonConstraintX/Y mais je ne comprends pas comment je peux le faire?

8
stack

Le problème est que vous ajoutez une nouvelle contrainte qui entre en conflit avec la contrainte existante.

Vous avez quelques options:

  1. En vigueur dans iOS 8, vous pouvez définir la propriété active sur false pour une contrainte avant d'ajouter une nouvelle contrainte.

  2. Dans les versions iOS antérieures à 8, vous souhaiterez supprimer les anciennes contraintes avant d'ajouter de nouvelles contraintes. 

  3. Idéalement, il est préférable de ne pas activer/désactiver (ou, pire, d'ajouter ou de supprimer) des contraintes, mais simplement de modifier la propriété constant d'une seule contrainte. Par exemple dans 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
        }
    
    }
    

    Quand l'effet souhaité peut être obtenu en modifiant la constant de la contrainte, c'est généralement mieux.

Pour la syntaxe Swift 2, voir révision précédente de cette réponse .

18
Rob

On dirait que vous ne faites qu'ajouter de plus en plus de contraintes. Vous ne pouvez pas faire cela parce qu'ils sont évidemment en conflit les uns avec les autres. Vous dites essentiellement "placez la vue à la position x = 1, x = 2, x = 3, x = 4 et x = 5".

Vous devez supprimer les anciennes contraintes. Vous avez deux options pour le faire.

  1. Enregistrez les contraintes dans un tableau et supprimez-les de la vue avant d'en ajouter de nouvelles. 

  2. Ou vous gardez une référence aux contraintes qui changent et ajustez leurs propriétés. 

Puisque vos contraintes ne diffèrent que par la valeur constante, vous devriez opter pour l’option 2. 

Faites de newButtonConstraintX et newButtonConstraintY une variable du viewController. 

par exemple.

class ViewController: UIViewController {
    var newButtonConstraintX: NSLayoutConstraint!
    var newButtonConstraintY: NSLayoutConstraint!


    func CreateButtonWithIndex(index:Int) {

        newButtonConstraintX = NSLayoutConstraint(item: newButton, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: CGFloat(riga))

        newButtonConstraintY = NSLayoutConstraint(item: newButton, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: CGFloat(colonna))

        /* ... */
    }

    func pan(rec:UIPanGestureRecognizer) {  
        /* ... */
        newButtonConstraintX.constant = line
        newButtonConstraintY.constant = column
        button.layoutIfNeeded()
    }
0
Matthias Bauch

Mettre à jour la réponse de Rob à Swift 3:

class ViewController: UIViewController {

    private var xConstraint: NSLayoutConstraint!
    private var yConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
    super.viewDidLoad()

    let label = UILabel()
    label.text = "x"
    label.setTranslatesAutoresizingMaskIntoConstraints(false)
    view.addSubview(label)

    // I don't really need to save references to these, so these are local variables


    let widthConstraint = NSLayoutConstraint(item: drugToDrugView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
    let heightConstraint = NSLayoutConstraint(item: drugToDrugView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)

    // but since I'll be modifying these later, these are class properties

    xConstraint = NSLayoutConstraint(item: drugToDrugView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0)
    yConstraint = NSLayoutConstraint(item: drugToDrugView, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 0)

    drugToDrugView.addConstraints([widthConstraint, heightConstraint, xConstraint, yConstraint])

    let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
    view.addGestureRecognizer(pan)

 }

 private var originalCenter: CGPoint!

 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
    view.setNeedsLayout()
}
0
Brian