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

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

问题描述

在我的代码中:
我在垂直scrollView中添加了5个按钮。每个按钮都被约束到scrollViews的顶部+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)修改其高度约束的常量属性 OR b)设置其高度约束的 isActive 属性到 false 然后给它一个新的高度约束 OR c)修改活动约束的优先级以使自动布局选择使用不同的约束。

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(内容大小)约束,以解释内容压缩内容拥抱 UIButton s抵制压缩,优先级 750 并阻止扩展优先级 250 self.height = 34(内容大小)约束显示为灰色,因为内容拥抱的优先级 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. 我更改了 shrink 方法以停用旧的高度约束,然后创建并激活一个新的约束。

  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. 设置常量高度约束的属性

  1. Setting the constant property of the height constraint

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

    注:F或者这个工作,你必须给高度约束一个初始优先级小于 1000 999 很好地工作)因为如果需要,自动布局将不允许您更改活动约束的优先级(优先级 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).

    OR

    // 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天全站免登陆