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

时间:2018-08-10 20:06:23

标签: ios swift autolayout intrinsic-content-size view-debugging

在下面的代码中: 我有5个按钮添加到垂直的scrollView中。每个按钮都限于scrollViews的顶部+ 20,前缘,后缘及其高度。我创建了一个b1HeightConstraint变量。在这里可以保存b1按钮的heightConstraint。

在单击按钮时,我试图删除此约束。但是我面临一个奇怪的问题:

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

enter image description here

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类中。开箱即用。我的代码是here

的代码的一部分

2 个答案:

答案 0 :(得分:7)

以下是关于您的代码的几点注释:

  1. 您从未向任何视图添加任何约束,所以您不应该删除它们。 iOS(CocoaTouch)在这些视图中添加了这些约束,因此请不要触摸它们。 (换句话说:当您未致电removeConstraint 时请勿致电addConstraint)。您对约束的控制是激活停用。将添加删除留在iOS上。
  2. 激活约束时,约束(通过iOS)被添加到约束中提到的两项的最常见祖先。因此,如果这两个视图是同级视图,则将其添加到父视图中。如果两个视图分别是父视图和子视图,则约束将被添加到父视图。如果两个视图分别是祖父母和孙子,则将其添加到祖父母中。如果这两个视图是第一个表亲,则将约束添加到其共同的祖父母中。
  3. 这些代码行:

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

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

  4. 如果要收缩按钮,则需要执行以下操作之一:a)修改其高度约束的constant属性 OR b)设置其高度约束的将isActive属性的false设置为self.height = 34 (content size),然后为其赋予新的高度约束 OR c)修改活动约束的优先级,以使 Auto Layout 选择使用不同的约束条件。

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


更新的代码:

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

  1. 我确保shrink是激活的约束。
  2. 我更改了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) } } 方法以停用旧的高度限制,然后创建并激活一个新的高度限制。

更新的代码

constant

缩小按钮高度的选项

  1. 设置高度约束的// shrink button's height by 200 points b1HeightConstraint?.constant -= 200 属性

    // make button height 20 points
    b1HeightConstraint?.isActive = false
    b1HeightConstraint = sender.heightAnchor.constraint(equalToConstant: 20)
    b1HeightConstraint?.isActive = true
    
  2. 停用旧约束并创建并激活新约束

    // 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)
    
  3. 更改高度限制的优先级

    1000

    注意:要使此方法起作用,您必须给高度约束一个小于999的初始优先级(1000工作得很好),因为 Auto Layout 不会让您如果需要,请更改活动约束的优先级(优先级// 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 )。

    OR

    @IBOutlet fileprivate weak var tblVw: UITableView!
    
    private var gradientLayer = CAGradientLayer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        if gradientLayer.superlayer != nil {
            gradientLayer.removeFromSuperlayer()
        }
        let topColor = UIColor(red: 16.0/255.0, green: 12.0/255.0, blue: 54.0/255.0, alpha: 1.0)
        let bottomColor = UIColor(red: 57.0/255.0, green: 33.0/255.0, blue: 61.0/255.0, alpha: 1.0)
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
        gradientLayer.colors = [topColor.cgColor, bottomColor.cgColor]
        gradientLayer.frame = tblVw.bounds
        let backgroundView = UIView(frame: tblVw.bounds)
        backgroundView.layer.insertSublayer(gradientLayer, at: 0)
        tblVw.backgroundView = backgroundView
    }
    

答案 1 :(得分:1)

这是因为视图和其superView之间的约束已添加到superView中,只有将高度/宽度约束静态添加到let arr = new Array(); object1 = { "Username" : "Jhon", "Console" : "xbox", "Pseudo" : "asd", } arr.push(object1) object2 = { "Username" : "Doe", "Console" : "ps4", "Pseudo" : "efg", } arr.push(object2) console.log(arr);时,您才能看到高度/宽度约束,请查阅Vandad IOS Book的该图

enter image description here

查看此Demo