如何将移出UIStackView的UIView放回原栈并恢复原有尺寸位置?
解决UIStackView子View全屏后无法恢复布局的问题
兄弟,我之前也踩过这个一模一样的坑!当你把UIStackView里的子View移出来做全屏显示时,StackView会自动丢掉这个View的布局关联信息,等你再塞回去的时候,它根本记不得这个View原来占多大位置、怎么排列——这就是问题根源!下面是我亲测有效的解决方案:
核心思路
在把View从StackView移走之前,提前保存它在StackView里的关键布局信息(位置索引、尺寸约束、优先级等),放回时再手动恢复这些信息,同时强制StackView重新计算布局。
具体实现步骤
1. 提前定义变量保存布局状态
先给你的ViewController加几个私有属性,用来存要移动的View的原始信息:
// 替换成你自己的View和StackView名称 private var targetView: UIView! private var parentStackView: UIStackView! // 用来保存原始布局信息 private var originalStackIndex: Int? private var originalWidthConstraint: NSLayoutConstraint? private var originalHeightConstraint: NSLayoutConstraint? private var originalVerticalHuggingPriority: UILayoutPriority? private var originalVerticalCompressionPriority: UILayoutPriority?
2. 全屏按钮的实现逻辑
点击全屏按钮时,先保存所有关键信息,再把View移到ViewController的根视图上设置全屏约束:
@IBAction func tapExpandButton(_ sender: UIButton) { // 1. 保存子View在StackView中的原始位置 originalStackIndex = parentStackView.arrangedSubviews.firstIndex(of: targetView) // 2. 保存原始尺寸约束(如果有设置固定宽高) originalWidthConstraint = targetView.constraints.first { $0.firstAttribute == .width } originalHeightConstraint = targetView.constraints.first { $0.firstAttribute == .height } // 3. 保存布局优先级(没固定宽高时,StackView靠这个分配空间) originalVerticalHuggingPriority = targetView.contentHuggingPriority(for: .vertical) originalVerticalCompressionPriority = targetView.contentCompressionResistancePriority(for: .vertical) // 4. 从StackView中移除(必须用removeArrangedSubview,不能直接removeFromSuperview) parentStackView.removeArrangedSubview(targetView) // 5. 添加到根视图并设置全屏约束 view.addSubview(targetView) targetView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ targetView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), targetView.leadingAnchor.constraint(equalTo: view.leadingAnchor), targetView.trailingAnchor.constraint(equalTo: view.trailingAnchor), targetView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) ]) // 6. 加个过渡动画更自然 UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } }
3. 还原按钮的实现逻辑
点击还原按钮时,先移除全屏约束,再把View放回StackView并恢复所有保存的布局信息:
@IBAction func tapShrinkButton(_ sender: UIButton) { // 1. 先把View从根视图移除 targetView.removeFromSuperview() // 2. 放回StackView的原始位置 if let index = originalStackIndex { parentStackView.insertArrangedSubview(targetView, at: index) } else { parentStackView.addArrangedSubview(targetView) } // 3. 恢复原始尺寸约束 if let widthConstraint = originalWidthConstraint { targetView.addConstraint(widthConstraint) } if let heightConstraint = originalHeightConstraint { targetView.addConstraint(heightConstraint) } // 4. 恢复布局优先级 if let huggingPriority = originalVerticalHuggingPriority { targetView.setContentHuggingPriority(huggingPriority, for: .vertical) } if let compressionPriority = originalVerticalCompressionPriority { targetView.setContentCompressionResistancePriority(compressionPriority, for: .vertical) } // 5. 强制StackView重新计算布局 parentStackView.setNeedsLayout() UIView.animate(withDuration: 0.3) { self.parentStackView.layoutIfNeeded() } // 6. 清空保存的信息,避免重复操作出错 originalStackIndex = nil originalWidthConstraint = nil originalHeightConstraint = nil originalVerticalHuggingPriority = nil originalVerticalCompressionPriority = nil }
额外注意事项
- 如果你处理的是水平方向的StackView,记得把代码里的
.vertical换成.horizontal,对应保存水平方向的优先级。 - 确保targetView内部的子View都是用AutoLayout布局的,没有约束冲突,不然放回后内部视图也会乱掉。
- 不要直接调用
targetView.removeFromSuperview()从StackView里移除,必须用removeArrangedSubview——StackView会维护自己的arrangedSubviews数组,直接移除会导致布局逻辑混乱。
这个方案我在多个项目里用过,基本都能完美解决问题,你可以根据自己的代码调整细节试试!
内容的提问来源于stack exchange,提问作者Jer
相关产品推荐
相关产品推荐

