为什么UIButton没有返回正确的约束?

在我的下面的代码中:我在垂直scrollView中添加了5个按钮。 每个按钮都被约束到scrollViews的顶部+20,前导,后缘及其高度。 我创建了一个b1HeightConstraint变量。 它是在那里保持b1按钮的heightConstraint。

在按钮单击中,我正在尝试删除此约束。 然而,我面临一个奇怪的问题:

当我记录约束时,我只看到2个约束,即使我已经添加了4个约束。 我的视图调试层次结构如下所示:

在此处输入图像描述

 import UIKit import Foundation class ViewController: UIViewController { var filterView: UIView! var scrollView: UIScrollView! var containerView: UIView! override func loadView() { filterView = UIView() view = filterView view.backgroundColor = #colorLiteral(red: 0.909803926944733, green: 0.47843137383461, blue: 0.643137276172638, alpha: 1.0) scrollView = UIScrollView() scrollView.backgroundColor = #colorLiteral(red: 0.474509805440903, green: 0.839215695858002, blue: 0.976470589637756, alpha: 1.0) view.addSubview(scrollView) scrollView.translatesAutoresizingMaskIntoConstraints = false scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true scrollView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 1).isActive = true scrollView.isScrollEnabled = true containerView = UIView() containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0) scrollView.addSubview(containerView) containerView.translatesAutoresizingMaskIntoConstraints = false // This is key: connect all four edges of the containerView to // to the edges of the scrollView containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true // Making containerView and scrollView the same height means the // content will not scroll vertically containerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true } let b1 = Buttons(titleText: "one") let b2 = Buttons(titleText: "two") let b3 = Buttons(titleText: "three") let b4 = Buttons(titleText: "four") let b5 = Buttons(titleText: "five") var b1HeightConstraint : NSLayoutConstraint? override func viewDidLoad() { super.viewDidLoad() let buttonArray = [b1, b2, b3, b4, b5] b1.button.addTarget(self, action: #selector(ViewController.shrink(_:)), for: .touchUpInside) var startPoint = containerView.topAnchor for btn in buttonArray { let theBtn = btn.button containerView.addSubview(theBtn) theBtn.translatesAutoresizingMaskIntoConstraints = false theBtn.topAnchor.constraint(equalTo: startPoint, constant: 20).isActive = true theBtn.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true theBtn.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true theBtn.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true startPoint = theBtn.bottomAnchor let btnHeight = theBtn.heightAnchor.constraint(equalTo: scrollView.heightAnchor) if btn == b1{ b1HeightConstraint = btnHeight } } containerView.bottomAnchor.constraint(equalTo: startPoint, constant: 20).isActive = true } @objc func shrink(_ sender: Any){ guard let btn = sender as? UIButton else{ return } print("count is: \(btn.constraints.count)") btn.removeConstraint(b1HeightConstraint!) containerView.removeConstraint(b1HeightConstraint!) print("count is: \(btn.constraints.count)") containerView.updateConstraintsIfNeeded() containerView.updateConstraints() scrollView.updateConstraintsIfNeeded() scrollView.updateConstraints() } } class Buttons : NSObject { let button = UIButton() init(titleText: String) { button.backgroundColor = #colorLiteral(red: 0.976470589637756, green: 0.850980401039124, blue: 0.549019634723663, alpha: 1.0) button.setTitle(titleText, for: .normal) } } 

代码已准备就绪,只能在ViewController类中转储。 开箱即用。 我的代码是这里编写的代码的衍生产品

以下是有关您的代码的几条评论:

  1. 您从未向任何视图添加任何约束,因此您不应删除它们。 iOS(CocoaTouch)将这些约束添加到这些视图中,因此请不要触摸它们。 (换句话说: 当你没有调用addConstraint时不要调用removeConstraint )。 您对约束的控制是激活停用它们。 将添加删除留给iOS。
  2. 激活约束时,会将约束(通过iOS)添加到约束中提到的两个项目的最常见祖先。 因此,如果两个视图是兄弟,它将被添加到父视图。 如果两个视图是父视图和子视图,则约束将添加到父视图。 如果两个视图是祖父母和孙子,它将被添加到祖父母。 如果两个视图是第一个堂兄弟,则约束将被添加到他们的共同祖父母。
  3. 这些代码行:

     let btnHeight = theBtn.heightAnchor.constraint(equalTo: scrollView.heightAnchor) if btn == b1{ b1HeightConstraint = btnHeight } 

    正在创建一个新约束并将其分配给b1HeightConstraint ,但您从未激活此约束,因此它根本没有添加到任何视图中。 因此,尝试删除它永远不会起作用,因为该约束仅存在于b1HeightConstraint属性中。 由于它从未被激活,它实际上并没有限制任何东西。

  4. 如果要缩小按钮,则需要执行以下操作之一:a)修改其高度约束的constant属性 b)将其高度约束的isActive属性设置为false ,然后为其赋予新的高度约束或者 c)修改自动布局选择使用不同约束的活动约束的优先级。

  5. 在视图调试层次结构中,显示的所有约束都是活动约束(意味着它们可供Auto Layout使用 )。 灰色的是Auto Layout选择不使用的,因为优先级较高的约束优先于它。 这不会引起冲突。 系统添加self.height = 34 (content size)约束以考虑内容压缩内容拥抱UIButton的抗压优先级为750 ,抗冲击优先级为250self.height = 34 (content size)约束显示为灰色,因为内容拥抱的优先级为250而另一个使用了更高优先级的约束(将按钮的高度设置为等于scrollView的高度的约束优先级为1000 )。


更新的代码:

这是您修改过的代码。 我改变了两件事:

  1. 我确保b1HeightConstraint是一个激活的约束。
  2. 我更改了shrink方法以停用旧的高度约束,然后创建并激活一个新的约束。

更新的代码

 import UIKit import Foundation class ViewController: UIViewController { var filterView: UIView! var scrollView: UIScrollView! var containerView: UIView! override func loadView() { filterView = UIView() view = filterView view.backgroundColor = #colorLiteral(red: 0.909803926944733, green: 0.47843137383461, blue: 0.643137276172638, alpha: 1.0) scrollView = UIScrollView() scrollView.backgroundColor = #colorLiteral(red: 0.474509805440903, green: 0.839215695858002, blue: 0.976470589637756, alpha: 1.0) view.addSubview(scrollView) scrollView.translatesAutoresizingMaskIntoConstraints = false scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true scrollView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 1).isActive = true scrollView.isScrollEnabled = true containerView = UIView() containerView.backgroundColor = #colorLiteral(red: 0.176470592617989, green: 0.498039215803146, blue: 0.756862759590149, alpha: 1.0) scrollView.addSubview(containerView) containerView.translatesAutoresizingMaskIntoConstraints = false // This is key: connect all four edges of the containerView to // to the edges of the scrollView containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true // Making containerView and scrollView the same height means the // content will not scroll vertically containerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true } let b1 = Buttons(titleText: "one") let b2 = Buttons(titleText: "two") let b3 = Buttons(titleText: "three") let b4 = Buttons(titleText: "four") let b5 = Buttons(titleText: "five") var b1HeightConstraint : NSLayoutConstraint? override func viewDidLoad() { super.viewDidLoad() let buttonArray = [b1, b2, b3, b4, b5] b1.button.addTarget(self, action: #selector(ViewController.shrink(_:)), for: .touchUpInside) var startPoint = containerView.topAnchor for btn in buttonArray { let theBtn = btn.button containerView.addSubview(theBtn) theBtn.translatesAutoresizingMaskIntoConstraints = false theBtn.topAnchor.constraint(equalTo: startPoint, constant: 20).isActive = true theBtn.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true theBtn.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true //theBtn.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true startPoint = theBtn.bottomAnchor let btnHeight = theBtn.heightAnchor.constraint(equalTo: scrollView.heightAnchor) btnHeight.isActive = true if btn == b1{ b1HeightConstraint = btnHeight } } containerView.bottomAnchor.constraint(equalTo: startPoint, constant: 20).isActive = true } @objc func shrink(_ sender: UIButton) { b1HeightConstraint?.isActive = false b1HeightConstraint = sender.heightAnchor.constraint(equalToConstant: 20) b1HeightConstraint?.isActive = true } } class Buttons : NSObject { let button = UIButton() init(titleText: String) { button.backgroundColor = #colorLiteral(red: 0.976470589637756, green: 0.850980401039124, blue: 0.549019634723663, alpha: 1.0) button.setTitle(titleText, for: .normal) } } 

缩小按钮高度的选项

  1. 设置高度约束的constant属性

     // shrink button's height by 200 points b1HeightConstraint?.constant -= 200 
  2. 停用旧约束并创建并激活一个新约束

     // make button height 20 points b1HeightConstraint?.isActive = false b1HeightConstraint = sender.heightAnchor.constraint(equalToConstant: 20) b1HeightConstraint?.isActive = true 
  3. 更改高度约束的优先级

     // Set b1HeightConstraint's priority to less than 250, and the // *content hugging* with priority 250 will take over and resize // the button to its intrinsic height b1HeightConstraint?.priority = UILayoutPriority(rawValue: 100) 

    注意:要使其工作,您必须为高度约束赋予小于1000的初始优先级( 999可以正常工作),因为如果需要, Auto Layout将不允许您更改活动约束的优先级(优先级1000 )。

    要么

     // Just deactivate the buttonHeight constraint and the *content // hugging* will take over and it will set the button's height // to its intrinsic height b1HeightConstraint?.isActive = false 

这是因为视图及其superView之间的约束被添加到superView,如果它们是静态添加到UIButton ,则只能看到高度/宽度约束,请从Vandad IOS Book查看此图

在此处输入图像描述

看到这个演示