这显然是实现中的一个错误UIStackView
(即系统错误)。
DonMag已经在他的评论中给出了指向正确方向的提示:
当您设置堆栈视图的spacing
为 0,一切都按预期进行。但是当您将其设置为任何其他值时,布局就会中断。
这是原因的解释:
ℹ️ 为了简单起见,我假设堆栈视图有
随着.fillProportionally
分配UIStackView
创建系统约束如下:
-
对于每个排列的子视图,它添加一个等宽约束(UISV-fill-proportionally
)与堆栈视图本身相关乘数:
arrangedSubview[i].width = multiplier[i] * stackView.width
如果你有n在堆栈视图中排列子视图,你会得到n这些限制。让我们称呼他们为proportionalConstraint[i]
(where i
表示各个视图在视图中的位置arrangedSubviews
array).
-
这些限制是不需要(即它们的优先级不是 1000)。相反,对第一个元素的约束arrangedSubviews
第一个数组的优先级为 999,第二个数组的优先级为 998,依此类推:
proportionalConstraint[0].priority = 999
proportionalConstraint[1].priority = 998
proportionalConstraint[2].priority = 997
proportionalConstraint[3].priority = 996
...
proportionalConstraint[n–1].priority = 1000 – n
这意味着所需的约束总是会克服这些比例限制!
-
为了连接排列的子视图(可能有间距),系统还创建n–1称为约束UISV-spacing
:
arrangedSubview[i].trailing + spacing = arrangedSubview[i+1].leading
这些限制是required(即优先级 = 1000)。
(系统还将创建一些其他约束(例如,对于垂直轴以及将第一个和最后一个排列的子视图固定到堆栈视图的边缘),但我不会在这里详细介绍,因为它们与理解什么无关出错了。)
苹果的文档 https://developer.apple.com/documentation/uikit/uistackviewdistribution/1616217-fillproportionally on the .fillProportionally
分布状态:
一种布局,其中堆栈视图调整其排列视图的大小,以便它们沿着堆栈视图的轴填充可用空间。视图根据其沿堆栈视图轴的内在内容大小按比例调整大小。
所以根据这个multiplier
为了proportionalConstraint
s 应计算如下for spacing = 0
:
-
totalIntrinsicWidth
= ∑i intrinsicWidth[i]
-
multiplier[i]
= intrinsicWidth[i]
/ totalIntrinsicWidth
如果我们排列的 10 个子视图都具有相同的固有宽度,则这将按预期工作:
multiplier[i] = 0.1
for all proportionalConstraint
s。然而,一旦我们将间距更改为非零值,则计算multiplier
变得更加复杂,因为必须考虑间距的宽度。我已经完成了数学计算和公式multiplier[i]
is:
Example:
对于堆栈视图配置如下:
- 堆栈视图宽度 = 400
- stackView.spacing = 2
上面的等式将得出:
multiplier[i] = 0.0955
您可以通过将其相加来证明这是正确的:
(10 * width) + (9 * spacing)
= (10 * multiplier * stackViewWidth) + (9 * spacing)
= (10 * 0.0955 * 400) + (9 * 2)
= (0.955 * 400) + 18
= 382 + 18
= 400
= stackViewWidth
然而,系统分配了不同的值:
multiplier[i] = 0.0917431
总宽度加起来为
(10 * width) + (9 * spacing)
= (10 * 0.0917431 * 400) + (9 * 2)
= 384,97
< stackViewWidth
明显地,这个值是错误的。
因此,系统必须打破约束。当然,它打破了最低优先级的约束,即proportionalConstraint
最后排列的子视图项目的。
这就是屏幕截图中最后排列的子视图被拉伸的原因。
如果您尝试不同的间距和堆栈视图宽度,您最终会得到各种看起来奇怪的布局。但他们都有一个共同点:间距始终优先。(如果将间距设置为更大的值,例如 30 或 40,您将只能看到前两个或三个排列的子视图,因为其余空间已完全被所需间距占据。)
总结一下:
The .fillProportionally
分发仅适用于spacing = 0
.
对于其他间距,系统会使用不正确的乘数创建约束。
这打破了布局
- 如果乘数小于应有的值,则必须拉伸任一排列的子视图(最后一个)
- 如果乘数大于应有的值,则必须压缩多个排列的子视图。
解决这个问题的唯一方法是“滥用”普通UIView
具有所需的固定宽度约束作为视图之间的间距。 (通常情况下,UILayoutGuide
为此目的引入了 s,但您甚至不能使用它们,因为您无法将布局指南添加到堆栈视图。)
恐怕由于这个错误,没有干净的解决方案可以做到这一点。