为什么UIButton没有返回正确的约束? [英] Why isn't UIButton returning correct constraints?
问题描述
在我的代码中:
我在垂直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:
- 您从未向任何人添加任何约束视图,所以你不应该删除它们。 iOS(CocoaTouch)将这些约束添加到这些视图中,因此请不要触摸它们。 (换句话说:当你没有调用
addConstraint
时,不要调用removeConstraint
)。您对约束的控制是激活和停用它们。保留添加和删除到iOS。 - 激活约束时,最多会添加一个约束(通过iOS)约束中提到的两个项的共同祖先。因此,如果两个视图是兄弟,它将被添加到父视图。如果两个视图是父视图和子视图,则约束将添加到父视图。如果两个视图是祖父母和孙子,它将被添加到祖父母。如果两个视图是第一个堂兄弟,则约束将被添加到他们的共同祖父母。
-
这些代码行:
- 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 calladdConstraint
). Your control over constraints is activating and deactivating them. Leave the adding and removing to iOS. - 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.
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. UIButton
s 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:
- 我确保
b1HeightConstraint
是一个激活的约束。 - 我更改了
shrink
方法以停用旧的高度约束,然后创建并激活一个新的约束。
- I made sure
b1HeightConstraint
was an activated constraint. - 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)
}
}
选项缩小按钮的高度
-
设置
常量
高度约束的属性
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屋!