空UIStackView引发布局歧义及iOS10适配技术问询
关于UIStackView空视图/隐藏子视图的Auto Layout歧义问题解析
这是个很典型的Auto Layout版本兼容问题,我来帮你梳理清楚背后的逻辑和解决方案:
一、为什么空StackView和含隐藏子视图的StackView表现不同?
核心原因在于UIStackView的固有尺寸(intrinsicContentSize)计算逻辑:
- 当StackView完全为空时,它没有任何可参考的子视图尺寸,因此Auto Layout无法推断出它的高度,导致高度和垂直位置歧义。同时你的
mainView底部依赖于StackView的顶部,StackView高度不确定的话,mainView的高度自然也会模糊。 - 当StackView中存在隐藏的子视图(比如你设置了
isHidden=true的UIButton),在iOS11及以后的系统中,UIStackView会自动将所有隐藏的子视图视为「尺寸为0」来计算自身的固有尺寸。这时候StackView的固有高度变成0,Auto Layout就能明确它的高度:顶部贴合mainView底部,底部贴合父视图底部,高度为0。反过来mainView的高度就被唯一确定为父视图的高度,所有歧义警告自然消失。
二、为什么iOS10的行为存在差异?
iOS10是UIStackView的早期版本,它的布局逻辑还不完善:
在iOS10中,即使子视图被设置为isHidden=true,StackView依然会保留该子视图的固有尺寸(比如UIButton默认的高度)来计算自身的尺寸。但因为子视图是隐藏的,Auto Layout无法确认这个尺寸是否应该被实际应用,导致StackView的高度和垂直位置依然存在歧义,警告也就无法消除。
三、如何在不破坏现有布局的前提下适配iOS10?
你可以通过添加低优先级的高度约束来解决歧义,同时不影响正常布局逻辑:
方法一:给StackView添加低优先级的0高度约束
给你的stackView添加一个高度为0的约束,并将优先级设为低于默认的required(比如.defaultLow),这样:
- 当StackView有可见子视图时,子视图的布局约束优先级更高,会自动覆盖这个0高度约束,StackView会正常展示内容;
- 当StackView所有子视图隐藏(或为空)时,这个0高度约束生效,明确StackView的高度为0,消除Auto Layout歧义。
代码实现如下:
// 在创建stackView并设置完基础属性后添加 let stackViewHeightConstraint = stackView.heightAnchor.constraint(equalToConstant: 0) stackViewHeightConstraint.priority = .defaultLow stackViewHeightConstraint.isActive = true
方法二:给mainView添加低优先级的底部贴合约束
给mainView添加一个底部贴合父视图的低优先级约束,和原有的stackView.topAnchor.constraint(equalTo: mainView.bottomAnchor)形成互补:
// 在激活原有约束的同时添加 NSLayoutConstraint.activate([ mainView.topAnchor.constraint(equalTo: view.topAnchor), mainView.trailingAnchor.constraint(equalTo: view.trailingAnchor), mainView.leadingAnchor.constraint(equalTo: view.leadingAnchor), stackView.topAnchor.constraint(equalTo: mainView.bottomAnchor), stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor), stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor), stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor), // 添加低优先级约束 mainView.bottomAnchor.constraint(equalTo: view.bottomAnchor).priority(.defaultLow) ])
这样当StackView高度为0时,mainView的底部会贴合父视图底部;当StackView有内容时,mainView的底部会被StackView的顶部拉上去,完全符合你的预期布局。
额外注意
如果你需要兼容iOS10,尽量避免依赖StackView对隐藏子视图的自动尺寸折叠逻辑,手动通过低优先级约束明确空状态下的尺寸是最稳妥的方案。
内容的提问来源于stack exchange,提问作者sch
相关产品推荐
相关产品推荐

