UIStackView - 根据孩子自动计算理想尺寸
Posted
技术标签:
【中文标题】UIStackView - 根据孩子自动计算理想尺寸【英文标题】:UIStackView - Auto calculate ideal size based on children 【发布时间】:2017-06-16 23:22:23 【问题描述】:在使用 Playground 以编程方式使用包含两个标签的 UIStackView
时,我意识到我执行堆栈视图的方式和标签不会自动计算它们的大小。那是我的(丑陋的)代码:
import UIKit
let label1 = UILabel()
let label2 = UILabel()
let arrangedViews: [UIView] = [label1, label2]
let view = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
label1.text = "Text 1"
label2.text = "Text 2"
for case let label as UILabel in arrangedViews
label.textColor = .white
label.sizeToFit()
view.backgroundColor = .black
let stackView = UIStackView(arrangedSubviews: arrangedViews)
stackView.axis = .vertical
stackView.distribution = .fill
stackView.layer.borderColor = UIColor.white.cgColor
stackView.layer.borderWidth = 1
var stackViewIdealSize = CGSize()
stackViewIdealSize.width = label1.intrinsicContentSize.width
arrangedViews.forEachlabel in stackViewIdealSize.height += label.intrinsicContentSize.height
stackView.frame.size = stackViewIdealSize
view.addSubview(stackView)
这段代码,除了做这样一个“简单的事情”很庞大之外,仍然以不正确的方式计算大小,因为我的游乐场视图是这样的:
有没有办法让UIStackView
甚至UIView
s 更聪明地计算理想尺寸?
【问题讨论】:
【参考方案1】:我很惊讶您通过设置堆栈视图的borderColor
和borderWidth
获得了任何效果。 UIStackView
被记录为 “a nonrendering subclass of UIView
; that is, it does not provide any user interface of its own.” 实际上它使用 CATransformLayer
作为它的层,CATransformLayer
文档说 “The CALayer
properties that are rendered by a layer are ignored, including: backgroundColor
, contents
, border style properties, stroke style properties, etc.”。
无论如何,因为它的alignment
是.fill
,所以堆栈视图会创建所需的优先级约束,强制其排列的子视图与自身具有相同的宽度。以下是这些限制:
<NSLayoutConstraint:0x60800008c210 'UISV-alignment' UILabel:0x7f821e4023d0'Text 1'.leading == UILabel:0x7f821e608ef0'Text 2'.leading (active)>
<NSLayoutConstraint:0x60800008c2b0 'UISV-alignment' UILabel:0x7f821e4023d0'Text 1'.trailing == UILabel:0x7f821e608ef0'Text 2'.trailing (active)>
<NSLayoutConstraint:0x60800008c080 'UISV-canvas-connection' UIStackView:0x7f821e40b790.leading == UILabel:0x7f821e4023d0'Text 1'.leading (active)>
<NSLayoutConstraint:0x60800008c1c0 'UISV-canvas-connection' H:[UILabel:0x7f821e4023d0'Text 1']-(0)-| (active, names: '|':UIStackView:0x7f821e40b790 )>
每个排列的子视图标签本身都有一个约束,将自己的宽度设置为其固有宽度。但是,默认情况下,此约束不是必需的优先级。以下是这些限制:
<NSContentSizeLayoutConstraint:0x6080000b7a00 UILabel:0x7fe385601720'Text 1'.width == 44.5 Hug:250 CompressionResistance:750 (active)>
<NSContentSizeLayoutConstraint:0x6180000abac0 UILabel:0x7fb809d0d810'Text 2'.width == 47 Hug:250 CompressionResistance:750 (active)>
这个约束有两个优先级(“Hug”和“CompressionResistance”)。两者都低于要求的优先级。 (要求的优先级为 1000。)
在没有其他更高优先级的约束的情况下,这些约束将使堆栈视图完全宽到足以容纳其最宽排列的子视图。
您省略的关键步骤是您没有设置stackView.translatesAutoresizingMaskIntoConstraints = false
。因为您没有将其设置为 false,所以堆栈视图具有将其位置和大小设置为现有 frame
的必需优先级约束。您将其现有的frame.size.width
设置为“Text 1”标签的固有宽度,但这比“Text 2”标签的固有宽度窄。所以堆栈视图给自己一个必需的优先宽度约束,反过来也控制“文本2”标签的宽度,迫使它比它的固有宽度更窄,所以它剪裁了它的文本。这是那个约束:
<NSLayoutConstraint:0x60800009a6d0 'UIView-Encapsulated-Layout-Width' UIStackView:0x7fb125c059d0.width == 44.5 (active)>
通过关闭translatesAutoresizingMaskIntoConstraints
,您告诉堆栈视图不要创建与其现有框架匹配的约束。如果您随后使用约束来设置堆栈视图的位置,而不是其大小,则其他约束(如前所述)将能够设置其大小以适应其排列的子视图。
所以这就是我写游乐场的方式:
import UIKit
import PlaygroundSupport
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.backgroundColor = #colorLiteral(red: 0.9568627477, green: 0.6588235497, blue: 0.5450980663, alpha: 1)
PlaygroundPage.current.liveView = view
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
for i in 1 ... 2
let label = UILabel()
label.text = "Text \(i)"
label.textColor = .white
stackView.addArrangedSubview(label)
view.addSubview(stackView)
view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
view.topAnchor.constraint(equalTo: stackView.topAnchor).isActive = true
要勾勒堆栈视图,我们可以添加另一个视图:
let stackOutlineView = UIView()
stackOutlineView.translatesAutoresizingMaskIntoConstraints = false
stackOutlineView.backgroundColor = .clear
stackOutlineView.layer.borderWidth = 1
stackOutlineView.layer.borderColor = UIColor.black.cgColor
view.addSubview(stackOutlineView)
stackView.leadingAnchor.constraint(equalTo: stackOutlineView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: stackOutlineView.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: stackOutlineView.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: stackOutlineView.bottomAnchor).isActive = true
结果如下:
【讨论】:
惊人的解释。以上是关于UIStackView - 根据孩子自动计算理想尺寸的主要内容,如果未能解决你的问题,请参考以下文章
UIStackView 与 superview 的宽度成比例
UIScrollView 不会滚动到子 UIStackView 的末尾