swift - UIStackView - 根据 child 自动计算理想尺寸

标签 swift uikit uistackview

在使用 Playground 以编程方式使用包含两个标签的 UIStackView 时,我意识到我处理堆栈 View 和标签的方式不会自动计算它们的大小。那是我的(丑陋的)代码:

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.forEach{label in stackViewIdealSize.height += label.intrinsicContentSize.height}
stackView.frame.size = stackViewIdealSize

view.addSubview(stackView)

这段代码,除了对于做这样一个“简单的事情”来说是巨大的,仍然以不正确的方式计算大小,因为我的 Playground View 是这样的:

enter image description here

有没有办法让 UIStackView 甚至 UIView 更智能地计算理想尺寸?

最佳答案

我很惊讶您设置堆栈 View 的 borderColorborderWidth 会产生任何效果。 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,stack view 创建了必需的优先级约束,强制其排列的 subview 与它自己的宽度相同。以下是这些约束:

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

每个排列的 subview 标签本身都有一个约束,将自己的宽度设置为其固有宽度。但是,默认情况下,此约束不是必需的优先级。以下是这些约束:

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

在没有其他更高优先级约束的情况下,这些约束将使堆栈 View 恰好足够宽以适应其最宽排列的 subview 。

您省略的关键步骤是您没有设置 stackView.translatesAutoresizingMaskIntoConstraints = false。因为您没有将其设置为 false,所以堆栈 View 具有将其位置和大小设置为其现有 frame 的必需优先级约束。您将其现有的 frame.size.width 设置为“Text 1”标签的固有宽度,但这比“Text 2”标签的固有宽度窄。因此,stack view 为自己提供了一个必需的优先级宽度约束,它反过来也控制了“Text 2”标签的宽度,迫使它比它的固有宽度窄,所以它剪裁了它的文本。这是该约束:

<NSLayoutConstraint:0x60800009a6d0 'UIView-Encapsulated-Layout-Width' UIStackView:0x7fb125c059d0.width == 44.5   (active)>

通过关闭 translatesAutoresizingMaskIntoConstraints,您告诉堆栈 View 不要创建匹配其现有框架的约束。如果您随后使用约束来设置堆栈 View 的位置,而不是其大小,则其他约束(如前所述)将能够设置其大小以适应其排列的 subview 。

下面是我编写 playground 的方式:

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

为了概述堆栈 View ,我们可以添加另一个 View :

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

结果如下:

playground result

关于swift - UIStackView - 根据 child 自动计算理想尺寸,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44599212/

相关文章:

iphone - 以编程方式更改带有动画的 UISwitch 的状态

ios - 如何使用 MKMapView.gestureRecognizers 访问特定的 UIGestureRecognizer

ios - 在 iOS9 上的 facebook sdk 4.6 中发送到 facebook 登录管理器实例的无法识别的选择器

iphone - unichar 和 NSString - 它们的互换性如何?

ios - 带有尺寸控制的标签旁边图像的快速/StackView 对齐

ios - 嵌套的 UIStackViews 在编程 UI 中不起作用

ios - UITableView 单元格中的 UIStackView 在滚动时会导致抖动

ios - 当我改变它的框架时,UISlider 没有改变它的宽度 - ios Swift

swift - SpriteKit 场景转换良好实践

iphone - 自定义 UIPageControl View ,用 "Page X of Y"替换点