iOS容器VC动态切换子ViewController时AutoLayout布局异常
问题原因
两个核心问题导致布局偏移和约束冲突:
- 最初的子控制器添加方法没有给动态加入的子控制器视图设置和父容器边缘绑定的约束,子控制器视图初始化时默认取全屏尺寸,AutoLayout基于这个错误的初始尺寸计算图片的高度和居中位置,后续父容器实际尺寸更新时没有触发约束重新计算,就会出现位置偏下的问题。
- 后续修改
addViewController方法后出现约束冲突,是因为通用方法给所有子控制器视图都加了撑满父视图的约束,但你用来承载内容的container控制器视图需要放在titleBar下方,不需要顶到主视图顶部,两个top约束同时存在就会冲突。另外你在ViewControllerB里手动调用view.layoutSubviews()是错误用法,这个方法是系统布局回调,手动调用不会触发正确的约束更新,反而会打乱布局时机。
修复方案
按以下步骤修改代码即可:
1. 修正子控制器管理扩展
替换Utilities文件中的UIViewController扩展,添加子控制器时自动完成边缘约束绑定,保证子控制器视图和父容器尺寸同步:
extension UIViewController { func addViewController(_ child: UIViewController) { addChild(child) view.addSubview(child.view) child.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ child.view.topAnchor.constraint(equalTo: view.topAnchor), child.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), child.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), child.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) child.didMove(toParent: self) } func remove() { guard parent != nil else { return } willMove(toParent: nil) view.removeFromSuperview() removeFromParent() } }
2. 调整container控制器的添加逻辑
因为container视图需要自定义位置(放在titleBar下方,不需要撑满主视图),不要用上面的通用扩展方法添加container,手动实现控制器容器逻辑,修改MainViewController的setup方法:
func setup() { titleBarView.backgroundColor = .gray view.addSubview(titleBarView) // 手动添加container容器控制器,自定义其视图位置 addChild(container) view.addSubview(container.view) container.didMove(toParent: self) showViewControllerA() }
保留原来layout方法里给container.view设置的位置约束即可,这部分不会再产生冲突。后续调用container.addViewController()切换A/B页面时,通用扩展会自动给A/B的视图加上撑满container的约束,保证子页面尺寸和容器实际尺寸一致。
3. 修正ViewControllerB的布局代码
删掉layout方法里手动调用view.layoutSubviews()的错误代码,补全imageView缺失的宽度约束(原约束只有居中、高度,约束不完整),不需要使用super.view,直接绑定当前控制器view即可:
func layout() { imageView.translatesAutoresizingMaskIntoConstraints = false imageView.contentMode = .scaleAspectFill imageView.layer.magnificationFilter = CALayerContentsFilter.nearest NSLayoutConstraint.activate([ imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor), imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor), imageView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.6), // 按你的图片比例设置宽度约束,这里以32:64的测试图比例为例 imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: 0.5), ]) }
修改完成后重新运行,动态切换到B页面时,子控制器视图会自动匹配container的实际bounds,imageView可以正常居中,也不会出现约束冲突报错。
内容的提问来源于stack exchange,提问作者RRR
相关产品推荐
相关产品推荐

