Swift更新约束
Swift Update Constraint
我向 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])
}
}
}
}
在我的项目中,我的用户可以移动这些按钮,然后我尝试向已识别的按钮添加更多约束,但我只是得到一个无穷无尽的错误约束
"<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>"
将尝试通过打破约束来恢复
"<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>"
将尝试通过打破约束来恢复
<NSLayoutConstraint:0x7f8e5a4e9510 UIButton:0x7f8e5a644f60'Button6'.centerY == UIView:0x7f8e5a648bc0.centerY - 40>
...我应该更新 newButtonConstraintX / Y 但我不明白我该怎么做吗?
看起来您只是在添加越来越多的约束。你不能那样做,因为它们显然相互冲突。你基本上是在说 "put the view at position x = 1, x = 2, x = 3, x = 4 and x = 5".
您必须删除旧的约束。您有两种选择。
将约束保存在数组中,并在添加新约束之前从视图中删除这些约束。
或者您保留对更改和调整其属性的约束的引用。
由于您的约束只是常数值不同,您应该选择选项 2。
使newButtonConstraintX
和newButtonConstraintY
成为viewController的变量。
例如
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()
}
问题是您添加的新约束与现有约束冲突。
您有几个选择:
在iOS 8中生效,您可以在添加新约束之前将约束的active
属性设置为false
。
在 8 之前的 iOS 版本中,您可能希望在添加新约束之前删除旧约束。
理想情况下,最好不必 activate/deactivate(或者更糟,添加和删除)约束,而只是修改 constant
属性一个单一的约束。例如 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
}
}
当可以通过修改constant
的约束达到预期的效果时,一般是最好的。
对于 Swift 2 语法,请参阅 previous revision of this answer。
更新 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()
}
我向 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])
}
}
}
}
在我的项目中,我的用户可以移动这些按钮,然后我尝试向已识别的按钮添加更多约束,但我只是得到一个无穷无尽的错误约束
"<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>"
将尝试通过打破约束来恢复
"<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>"
将尝试通过打破约束来恢复
<NSLayoutConstraint:0x7f8e5a4e9510 UIButton:0x7f8e5a644f60'Button6'.centerY == UIView:0x7f8e5a648bc0.centerY - 40>
...我应该更新 newButtonConstraintX / Y 但我不明白我该怎么做吗?
看起来您只是在添加越来越多的约束。你不能那样做,因为它们显然相互冲突。你基本上是在说 "put the view at position x = 1, x = 2, x = 3, x = 4 and x = 5".
您必须删除旧的约束。您有两种选择。
将约束保存在数组中,并在添加新约束之前从视图中删除这些约束。
或者您保留对更改和调整其属性的约束的引用。
由于您的约束只是常数值不同,您应该选择选项 2。
使newButtonConstraintX
和newButtonConstraintY
成为viewController的变量。
例如
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()
}
问题是您添加的新约束与现有约束冲突。
您有几个选择:
在iOS 8中生效,您可以在添加新约束之前将约束的
active
属性设置为false
。在 8 之前的 iOS 版本中,您可能希望在添加新约束之前删除旧约束。
理想情况下,最好不必 activate/deactivate(或者更糟,添加和删除)约束,而只是修改
constant
属性一个单一的约束。例如 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 } }
当可以通过修改
constant
的约束达到预期的效果时,一般是最好的。
对于 Swift 2 语法,请参阅 previous revision of this answer。
更新 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()
}