如何在UIScrollView中添加子视图控制器并对齐顶部安全区
问题核心原因
- 代码同时设置了
UIScrollView的frame又开启了translatesAutoresizingMaskIntoConstraints = false,手动设置的frame会被自动约束完全覆盖,原有约束绑定了主视图的topAnchor而非安全区顶部,导致ScrollView本身延伸到了刘海区域,内容自然被遮挡 - 子视图控制器的View采用手动计算frame的方式布局,没有和ScrollView的内容布局、系统安全区规则联动,约束设置错误就会出现滚动时视图层级错位的问题
修正方案
全部采用自动布局实现,兼容安全区适配,不会出现滚动错位问题:
func setupScrollView() { scrollView = UIScrollView() scrollView.isPagingEnabled = true scrollView.showsVerticalScrollIndicator = false scrollView.bounces = false scrollView.translatesAutoresizingMaskIntoConstraints = false self.view.addSubview(scrollView) // 方案1:ScrollView本身对齐安全区,滚动范围完全在安全区内,内容不会被刘海遮挡 NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), scrollView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor), scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor) ]) // 用ScrollView的Content Layout Guide绑定子视图布局,不需要手动计算contentSize let contentGuide = scrollView.contentLayoutGuide let frameGuide = scrollView.frameLayoutGuide // 添加子VC addChild(topVc) addChild(middleVc) addChild(bottomVc) let childViews = [topVc.view, middleVc.view, bottomVc.view] childViews.forEach { $0?.translatesAutoresizingMaskIntoConstraints = false scrollView.addSubview($0!) // 每个子视图宽高和ScrollView可视范围一致 NSLayoutConstraint.activate([ $0!.widthAnchor.constraint(equalTo: frameGuide.widthAnchor), $0!.heightAnchor.constraint(equalTo: frameGuide.heightAnchor) ]) } // 垂直方向排布三个子视图 NSLayoutConstraint.activate([ topVc.view.topAnchor.constraint(equalTo: contentGuide.topAnchor), topVc.view.leadingAnchor.constraint(equalTo: contentGuide.leadingAnchor), topVc.view.trailingAnchor.constraint(equalTo: contentGuide.trailingAnchor), middleVc.view.topAnchor.constraint(equalTo: topVc.view.bottomAnchor), middleVc.view.leadingAnchor.constraint(equalTo: contentGuide.leadingAnchor), middleVc.view.trailingAnchor.constraint(equalTo: contentGuide.trailingAnchor), bottomVc.view.topAnchor.constraint(equalTo: middleVc.view.bottomAnchor), bottomVc.view.leadingAnchor.constraint(equalTo: contentGuide.leadingAnchor), bottomVc.view.trailingAnchor.constraint(equalTo: contentGuide.trailingAnchor), bottomVc.view.bottomAnchor.constraint(equalTo: contentGuide.bottomAnchor) ]) // 子VC生命周期回调 topVc.didMove(toParent: self) middleVc.didMove(toParent: self) bottomVc.didMove(toParent: self) // 默认定位到中间页 scrollView.contentOffset.y = scrollView.frame.height }
可选调整
如果你希望滚动时内容可以穿透到刘海/底部安全条区域(比如背景色可以延伸过去),只需要修改ScrollView的约束绑定到父视图而非安全区,然后在三个子VC内部,将自身的内容视图绑定到自身View的safeAreaLayoutGuide即可,父级ScrollView的布局逻辑不需要改动。
注意事项
- 开启
translatesAutoresizingMaskIntoConstraints = false后不要手动设置控件的frame,所有布局规则通过约束实现 - iOS 11以上的UIScrollView提供的
contentLayoutGuide和frameLayoutGuide可以自动计算contentSize,不需要手动赋值,适配横竖屏切换更稳定
内容的提问来源于stack exchange,提问作者Jav Solo
相关产品推荐
相关产品推荐

