自动布局根据可用屏幕尺寸在给定范围内调整 UIbutton 的高度
Autolayout adjust height of UIbutton within given range based on available screen size
我正在尝试使用 Autolayout(以编程方式)生成以下场景。如图所示,按钮的高度在任何屏幕尺寸下都不能大于 56 且小于 44。顶部内容视图,根据其内部内容有自己的大小,但其顶部锚点的最小顶部填充必须为 8。它可以大于 8。
按钮和内容视图之间的填充是严格的 8pts。底部锚点的底部填充是严格的 12。
我唯一可以玩的是按钮的高度来调整小屏幕中的所有内容。
尝试
1) 我尝试将所有按钮放在堆栈视图中,并对每个按钮都设置了高度限制。
button.heightAnchor.constraint(lessThanOrEqualToConstant: 56).isActive = true
button.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).isActive = true
但按钮高度不会增加超过 44。现在,当我在主视图和顶视图之间设置顶部约束时,它可以正常工作,严格设置为 8
contentView.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true
而不是
contentView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 8).isActive = true
但这不适用于更大的屏幕,因为顶视图需要比顶视图更靠近按钮。
2) 当我不使用堆栈视图,并为每个按钮设置高度约束时,无论是哪个设备,它都不会增加超过 44。如果我删除大于 44 的约束,然后按钮的大小减少到 36。
我尝试将优先级设置为 topmost 大于 8 约束以及按钮约束的高度,以及设置内容视图和顶部按钮之间的宽高比约束,然后将所有其他按钮高度设置为顶部按钮,但又一次, 都只是坚持到 44.
请告知或提供解决此问题的方法。
如果你对自己 "talk it out" 这真的很公平 straight-forward。
简单的部分:
- 堆栈视图受限
8-pts
前导和尾随
16-pts
从底部
>= 8-pts
从顶部
- 每个按钮的高度限制为
>= 44
<= 56
- 顶部 "dynamic" 视图的高度由其内容决定
"tricky"部分:
- 向堆栈视图添加另一个顶部约束
= 8-pts
与 .priority = .defaultHigh
- 告诉 auto-layout 到 "pull the top of the stack view up" 除非它是 "pulled down"(通过按钮的 max-heights)
- 将按钮 2、3 和 4 的高度限制为等于按钮 1 的高度
这是一个完整的示例(所有代码,没有故事板连接)。 "top" 视图将从 240 点高开始...每次点击第一个按钮时,顶视图的高度将增加 20,最大为 400。顶视图也将显示新的高度:
class TestViewController: UIViewController {
let stackView: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .vertical
v.spacing = 8
return v
}()
let dynaView: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .yellow
v.textAlignment = .center
v.numberOfLines = 0
return v
}()
var dynaViewHeightConstraint: NSLayoutConstraint!
var theButtons: [UIButton] = [UIButton]()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// stackView constrained 8-pts Leading and Trailing
// 16-pts from bottom
// >= 8-pts from top
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -16.0),
stackView.topAnchor.constraint(greaterThanOrEqualTo: g.topAnchor, constant: 8.0),
])
// we need a stackView TOP anchor = 8 with priorty 750
// this will "pull" the top up as far as it can, without violating the
// button height constraints
let stackTop = stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0)
stackTop.priority = .defaultHigh
stackTop.isActive = true
for i in 1...4 {
let b = UIButton()
b.translatesAutoresizingMaskIntoConstraints = false
b.setTitle("Button \(i)", for: [])
b.backgroundColor = .red
NSLayoutConstraint.activate([
// button heights are min: 44 max: 56
b.heightAnchor.constraint(lessThanOrEqualToConstant: 56.0),
b.heightAnchor.constraint(greaterThanOrEqualToConstant: 44.0),
])
theButtons.append(b)
}
// let's start with the dynaView height at 240
dynaViewHeightConstraint = dynaView.heightAnchor.constraint(equalToConstant: 240.0)
dynaViewHeightConstraint.isActive = true
// add dynsView to the stack
stackView.addArrangedSubview(dynaView)
guard let firstButton = theButtons.first else {
fatalError("Something is wrong with my setup!")
}
// add each button to the stack,
// setting its height equal to the first button's height
theButtons.forEach { b in
stackView.addArrangedSubview(b)
if b != theButtons.first {
b.heightAnchor.constraint(equalTo: firstButton.heightAnchor).isActive = true
}
}
// tapping the first button will increae dynaView's height
firstButton.addTarget(self, action: #selector(self.didTap(_:)), for: .touchUpInside)
}
override func viewDidAppear(_ animated: Bool) {
showStats()
}
func showStats() -> Void {
var s = "DynaView Height: \(dynaView.frame.height)"
for i in 0..<theButtons.count {
let b = theButtons[i]
s += "\n"
s += "Button \(i + 1) Height: \(b.frame.height)"
}
dynaView.text = s
}
@objc
func didTap(_ sender: Any) -> Void {
let h = min(dynaViewHeightConstraint.constant + 20, 400)
dynaViewHeightConstraint.constant = h
DispatchQueue.main.async {
self.showStats()
}
}
}
这是它在 iPhone 8...
上的样子
在 iPhone 11 上 top-view 高度最大为 400:
我正在尝试使用 Autolayout(以编程方式)生成以下场景。如图所示,按钮的高度在任何屏幕尺寸下都不能大于 56 且小于 44。顶部内容视图,根据其内部内容有自己的大小,但其顶部锚点的最小顶部填充必须为 8。它可以大于 8。 按钮和内容视图之间的填充是严格的 8pts。底部锚点的底部填充是严格的 12。 我唯一可以玩的是按钮的高度来调整小屏幕中的所有内容。
尝试 1) 我尝试将所有按钮放在堆栈视图中,并对每个按钮都设置了高度限制。
button.heightAnchor.constraint(lessThanOrEqualToConstant: 56).isActive = true
button.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).isActive = true
但按钮高度不会增加超过 44。现在,当我在主视图和顶视图之间设置顶部约束时,它可以正常工作,严格设置为 8
contentView.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true
而不是
contentView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 8).isActive = true
但这不适用于更大的屏幕,因为顶视图需要比顶视图更靠近按钮。
2) 当我不使用堆栈视图,并为每个按钮设置高度约束时,无论是哪个设备,它都不会增加超过 44。如果我删除大于 44 的约束,然后按钮的大小减少到 36。 我尝试将优先级设置为 topmost 大于 8 约束以及按钮约束的高度,以及设置内容视图和顶部按钮之间的宽高比约束,然后将所有其他按钮高度设置为顶部按钮,但又一次, 都只是坚持到 44.
请告知或提供解决此问题的方法。
如果你对自己 "talk it out" 这真的很公平 straight-forward。
简单的部分:
- 堆栈视图受限
8-pts
前导和尾随16-pts
从底部>= 8-pts
从顶部
- 每个按钮的高度限制为
>= 44
<= 56
- 顶部 "dynamic" 视图的高度由其内容决定
"tricky"部分:
- 向堆栈视图添加另一个顶部约束
= 8-pts
与.priority = .defaultHigh
- 告诉 auto-layout 到 "pull the top of the stack view up" 除非它是 "pulled down"(通过按钮的 max-heights)
- 将按钮 2、3 和 4 的高度限制为等于按钮 1 的高度
这是一个完整的示例(所有代码,没有故事板连接)。 "top" 视图将从 240 点高开始...每次点击第一个按钮时,顶视图的高度将增加 20,最大为 400。顶视图也将显示新的高度:
class TestViewController: UIViewController {
let stackView: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .vertical
v.spacing = 8
return v
}()
let dynaView: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .yellow
v.textAlignment = .center
v.numberOfLines = 0
return v
}()
var dynaViewHeightConstraint: NSLayoutConstraint!
var theButtons: [UIButton] = [UIButton]()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// stackView constrained 8-pts Leading and Trailing
// 16-pts from bottom
// >= 8-pts from top
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -16.0),
stackView.topAnchor.constraint(greaterThanOrEqualTo: g.topAnchor, constant: 8.0),
])
// we need a stackView TOP anchor = 8 with priorty 750
// this will "pull" the top up as far as it can, without violating the
// button height constraints
let stackTop = stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0)
stackTop.priority = .defaultHigh
stackTop.isActive = true
for i in 1...4 {
let b = UIButton()
b.translatesAutoresizingMaskIntoConstraints = false
b.setTitle("Button \(i)", for: [])
b.backgroundColor = .red
NSLayoutConstraint.activate([
// button heights are min: 44 max: 56
b.heightAnchor.constraint(lessThanOrEqualToConstant: 56.0),
b.heightAnchor.constraint(greaterThanOrEqualToConstant: 44.0),
])
theButtons.append(b)
}
// let's start with the dynaView height at 240
dynaViewHeightConstraint = dynaView.heightAnchor.constraint(equalToConstant: 240.0)
dynaViewHeightConstraint.isActive = true
// add dynsView to the stack
stackView.addArrangedSubview(dynaView)
guard let firstButton = theButtons.first else {
fatalError("Something is wrong with my setup!")
}
// add each button to the stack,
// setting its height equal to the first button's height
theButtons.forEach { b in
stackView.addArrangedSubview(b)
if b != theButtons.first {
b.heightAnchor.constraint(equalTo: firstButton.heightAnchor).isActive = true
}
}
// tapping the first button will increae dynaView's height
firstButton.addTarget(self, action: #selector(self.didTap(_:)), for: .touchUpInside)
}
override func viewDidAppear(_ animated: Bool) {
showStats()
}
func showStats() -> Void {
var s = "DynaView Height: \(dynaView.frame.height)"
for i in 0..<theButtons.count {
let b = theButtons[i]
s += "\n"
s += "Button \(i + 1) Height: \(b.frame.height)"
}
dynaView.text = s
}
@objc
func didTap(_ sender: Any) -> Void {
let h = min(dynaViewHeightConstraint.constant + 20, 400)
dynaViewHeightConstraint.constant = h
DispatchQueue.main.async {
self.showStats()
}
}
}
这是它在 iPhone 8...
上的样子在 iPhone 11 上 top-view 高度最大为 400: