I added the constraint to the buttons created in my UIView
func CreateButtonWithIndex(index:Int) {
newButton.setTranslatesAutoresizingMaskI
The issue is that you're adding a new constraint that conflicts with the existing constraint.
You have a few options:
Effective in iOS 8, you can set the active
property to false
for a constraint before you add a new constraint.
In iOS versions prior to 8, you would want to remove the old constraints before adding new constraints.
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.
Looks like you are just adding more and more constraint. You can't do that because they obviously conflict with each other. You are basically saying "put the view at position x = 1, x = 2, x = 3, x = 4 and x = 5".
You have to remove the old constraints. You have two options to do that.
Save the constraints in an array, and remove these constraints from the view before adding new ones.
Or you keep a reference to the constraints that change and adjust their properties.
Since your constraints just differ in the constant value you should go for option 2.
Make newButtonConstraintX
and newButtonConstraintY
a variable of the viewController.
e.g.
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()
}
Update Rob's Answer to 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()
}