为什么 UIButton 不返回正确的约束? [英] Why isn't UIButton returning correct constraints?

查看:22
本文介绍了为什么 UIButton 不返回正确的约束?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我下面的代码中:我在垂直滚动视图中添加了 5 个按钮.每个按钮都被限制在滚动视图的 top + 20 、前缘、后缘及其高度.我创建了一个 b1HeightConstraint 变量.它用于保存 b1 按钮的 heightConstraint.

In my code below: I have 5 buttons added into a vertical scrollView. Each button is constrained to the scrollViews's top + 20 ,leading, trailing edges and its height. I have created a b1HeightConstraint variable. It's there to hold the heightConstraint of the b1 button.

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

In a button click, I'm trying to remove this constraint. Yet I'm facing an odd issue:

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

When I log the constraints I only see 2 constraints, even though I've added 4 constraints to it. My the view debug hierarchy is like below:

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 are several comments about your code:

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

  1. You never added any constraints to any views, so you shouldn't be removing them. iOS (CocoaTouch) added those constraints to those views, so please don't touch them. (In other words: don't call removeConstraint when you didn't call addConstraint). Your control over constraints is activating and deactivating them. Leave the adding and removing to iOS.
  2. When you activate a constraint, a constraint is added (by iOS) to the most common ancestor of the two items mentioned in the constraint. So if the two views are siblings, it will be added to the parent. If the two views are parent and child, the constraint will be added to the parent. If the two views are grandparent and grandchild, it will be added to the grandparent. If the two views are first cousins, the constraint will be added to their common grandparent.
  3. These lines of code:

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

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

are creating a new constraint and assigning it to b1HeightConstraint, but you never activated this constraint, so it hasn't have been added to any view at all. So trying to remove it was never going to work, because that constraint exists only in your b1HeightConstraint property. Since it was never activated, it isn't actually constraining anything.

如果你想缩小一个按钮,你需要做以下事情之一:a) 修改其高度约束的 constant 属性 OR b) 设置其高度约束的 isActive 属性为 false 然后给它一个新的高度约束 OR c) 修改活动约束的优先级以具有 Auto Layout选择使用不同的约束.

If you want to shrink a button, you need to do one of these: a) modify the constant property of its height constraint OR b) set its height constraint's isActive property to false and then give it a new height constraint OR c) modify the priorities of the active constraints to have Auto Layout choose to use different constraints.

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

In your view debug hierarchy, all the constraints shown are active constraints (meaning they are available to be used by Auto Layout). The grayed out ones are the ones Auto Layout chose not to use because a higher priority constraint had precedence over it. This causes no conflict. The self.height = 34 (content size) constraint is added by the system to account for content compression and content hugging. UIButtons resist compression with priority 750 and resist expansion with priority 250. The self.height = 34 (content size) constraint is grayed out because content hugging has a priority of 250 and another higher priority constraint was used instead (the constraint which sets the button's height equal to the scrollView's height has priority 1000).

<小时>

更新代码:

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

Here is your modified code. I changed two things:

  1. 我确定 b1HeightConstraint 是一个激活的约束.
  2. 我更改了 shr​​ink 方法以停用旧的高度约束,然后创建并激活一个新的.
  1. I made sure b1HeightConstraint was an activated constraint.
  2. I changed the shrink method to deactivate the old height constraint and then create and activate a new one.

更新代码

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

  • 停用旧约束并创建并激活新约束

    // make button height 20 points
    b1HeightConstraint?.isActive = false
    b1HeightConstraint = sender.heightAnchor.constraint(equalToConstant: 20)
    b1HeightConstraint?.isActive = true
    

  • 改变高度约束的优先级

    // 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).

    Note: For this to work, you have to give the height constraint an initial priority less than 1000 (999 works nicely) because Auto Layout will not let you change the priority of an active constraint if it is required (priority 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
    

  • 这篇关于为什么 UIButton 不返回正确的约束?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆