例如,假设您有一个垂直堆栈视图。它的对齐方式是领先的(因为您希望大多数项目向左收缩)。
但是,您有一种特殊类型的项目,您想要全宽。
我总是采用某种复杂的解决方案来实现这一目标:
class FullWidthThing: .. {
..
translatesAutoresizingMaskIntoConstraints = false
private lazy var fullWidth: NSLayoutConstraint = {
guard let sv = superview else { return NSLayoutConstraint() }
let v = widthAnchor.constraint(equalTo: sv.widthAnchor)
v.isActive = true
return v
}()
override func layoutSubviews() {
if superview != nil {
_ = fullWidth
}
super.layoutSubviews()
}
}
但这实际上就像将内在函数设置为
.greatestFiniteMagnitude
一样简单吗?表面测试表明这似乎有效:
private lazy var setup: () = {
translatesAutoresizingMaskIntoConstraints = false
setContentCompressionResistancePriority(.required, for: .horizontal)
...
return ()
}()
override var intrinsicContentSize: CGSize {
var sz = super.intrinsicContentSize
sz.width = .greatestFiniteMagnitude
return sz
}
有人明确知道这是否有效,或者这样做有问题,或者这样做的常见方法吗?
对于任何视图,自动布局(无论是在堆栈视图还是其他层次结构中)都将使用 .intrinsicContentSize,除非其他因素影响大小。
考虑这个布局:
黄色框架是垂直堆栈视图,具有顶部和前导约束,间距:12 和对齐方式:前导
每个轮廓框架也是一个垂直堆栈视图,分布:均匀填充,间距:4,对齐方式:前导,包含一个标签和一个(默认)
UIView
。
我们看不到视图,因为它们没有宽度。
每个“子”堆栈视图的宽度与其标签的固有宽度相匹配,并且外部堆栈视图具有最宽的“子”堆栈视图的宽度。
Outer Stack width: 300.0 (intrinsic width of widest label)
现在,让我们添加另一个子堆栈视图,其标签太宽而无法适应屏幕:
Outer Stack width: 833.6666666666666
接下来,让我们向外部堆栈视图添加一个 Trailing 约束:
Outer Stack width: 353.0
并且,第四根弦较长:
Outer Stack width: 353.0 (still)
那么,如果我们使用子类“宽”视图而不是默认的
UIView
,会发生什么:
class WideView: UIView {
override var intrinsicContentSize: CGSize {
var sz = super.intrinsicContentSize
sz.width = .greatestFiniteMagnitude
return sz
}
}
.greatesFiniteMagnitude
的值是1.7976931348623157e+308
...如果我们将其格式化为浮点数(“%0.0f”),我们会得到一个长度为309位的数字。
这是我们通过前 3 个字符串得到的结果,外部堆栈视图上没有尾随锚点:
Outer Stack width: 2777777.0 (greatest width allowed by UIKit)
如果我们添加更长的字符串:
看起来一样,再说一遍:
Outer Stack width: 2777777.0
添加外部堆栈视图尾随锚点:
Outer Stack width: 353.0
这是我用来生成这些输出的类(请参阅注释和注释行):
class WideTestVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
let strs: [String] = [
"Short",
"Medium length",
"Longer string for width testing",
//"This string will be too long to fit the available width of a phone in portrait orientation!",
]
let colors: [UIColor] = [
.systemRed,
.systemGreen,
.systemBlue,
.cyan,
]
let outerStack = UIStackView()
outerStack.axis = .vertical
outerStack.alignment = .leading
outerStack.spacing = 12
for (str, c) in zip(strs, colors) {
let st = UIStackView()
st.axis = .vertical
st.alignment = .leading
st.distribution = .fillEqually
st.spacing = 4
st.layer.borderColor = UIColor.darkGray.cgColor
st.layer.borderWidth = 1
let label = UILabel()
label.font = .systemFont(ofSize: 24.0, weight: .light)
label.text = str
label.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
// to see the difference...
// use standard UIView (will have no width)
let v = UIView()
// or, use custom "WideView" with width: .greatestFiniteMagnitude
//let v = WideView()
v.backgroundColor = c
st.addArrangedSubview(label)
st.addArrangedSubview(v)
outerStack.addArrangedSubview(st)
}
outerStack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(outerStack)
let g = view.safeAreaLayoutGuide
// constrain ONLY Top / Leading, or
// constrain Top / Leading / Trailing
NSLayoutConstraint.activate([
outerStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 30.0),
outerStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
//outerStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
])
outerStack.backgroundColor = .yellow
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let v = view.subviews.first as? UIStackView
else { return }
print(v.frame.width)
}
}