为何UIStackView的fillProportionally布局属性未按预期工作?
UIStackView.fillProportionally布局下按钮高度不一致问题分析与解决
问题现象
在垂直方向的UIStackView中放置两个标题、字体、字号完全一致的按钮,设置distribution = .fillProportionally时,两个按钮高度出现明显差异(如94pt和86pt);但使用.fillEqually布局时高度完全一致,且将StackView的spacing设为0时,.fillProportionally布局下两个按钮高度也恢复一致。
通过代码打印帧高度验证该现象:
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() print("button 1 height: \(button1.frame.height)") print("button 2 height: \(button2.frame.height)") }
终端输出:
button 1 height: 94.0 button 2 height: 86.0 button 1 height: 94.0 button 2 height: 86.0
复现代码
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let buttonOne = UIButton() buttonOne.titleLabel?.font = UIFont(name: "Marker Felt Wide", size: 17.0) buttonOne.backgroundColor = .red buttonOne.setTitle("Pirate Handbook", for: .normal) let buttonTwo = UIButton() buttonTwo.titleLabel?.font = UIFont(name: "Marker Felt Wide", size: 17.0) buttonTwo.backgroundColor = .blue buttonTwo.setTitle("Pirate Handbook", for: .normal) buttonOne.translatesAutoresizingMaskIntoConstraints = false buttonTwo.translatesAutoresizingMaskIntoConstraints = false let stackView = UIStackView(arrangedSubviews: [buttonTwo, buttonOne]) stackView.axis = .vertical stackView.translatesAutoresizingMaskIntoConstraints = false stackView.distribution = .fillProportionally stackView.alignment = .fill stackView.spacing = 8 stackView.backgroundColor = .green view.addSubview(stackView) NSLayoutConstraint.activate([ stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 110), stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 23), stackView.widthAnchor.constraint(equalToConstant: 154), stackView.heightAnchor.constraint(equalToConstant: 188) ]) } override func viewDidLayoutSubviews() { view.subviews.first?.subviews.forEach { print("height \($0.frame.height)") } } }
代码运行后终端输出:
height 0.0 height 0.0 height 84.33333333333333 height 95.66666666666667
原因解析
fillProportionally的核心逻辑是按子视图intrinsic content size的比例分配剩余空间。当StackView设置固定高度且包含spacing时,系统会先扣除spacing,再将剩余空间按比例分配给子视图。
尽管两个按钮的标题和字体完全一致,但UIButton的intrinsic content size计算不仅依赖标题,还受内部默认的content inset、title edge inset等属性影响,可能存在极细微的浮点精度差异。扣除spacing后,剩余空间的比例分配会放大这个微小差异,最终导致按钮高度出现明显差值。
而fillEqually是直接将剩余空间平均分配,不受intrinsic size比例影响,因此高度一致;当spacing设为0时,剩余空间分配无额外扣除项,微小的intrinsic size差异被抵消,高度也能保持一致。
解决方案
- 使用
.fillEqually替代:如果需求是子视图高度完全一致,.fillEqually是更直接可靠的选择,无需依赖intrinsic size计算。 - 添加固定高度约束:给两个按钮添加明确的高度约束(如
button.heightAnchor.constraint(equalToConstant: 94)),强制高度一致,覆盖fillProportionally的比例分配逻辑。 - 统一intrinsic size:手动设置两个按钮相同的content inset、title edge inset,消除intrinsic size的细微差异;或取消StackView的固定高度,让其自适应内容高度。
内容的提问来源于stack exchange,提问作者iosdev
相关产品推荐
相关产品推荐

