iPhone X中UIScrollView的contentSize高度无法正常更新问题求助
我之前也踩过类似嵌套滚动的坑,尤其是在iPhone X这类带安全区域的设备上,你的问题大概率是布局时机不对+系统自动调整内边距干扰导致的,给你几个可行的解决方案:
确保在布局完成后计算contentSize
你直接用subviews.last?.frame计算的问题在于,Auto Layout可能还没完成更新,尤其是iPhone X的安全区域布局会让系统多走一轮布局流程,导致你读取到的frame是旧值。建议把contentSize的更新放到layoutSubviews()方法里,这是系统确保所有布局都完成后才会调用的时机:override func layoutSubviews() { super.layoutSubviews() guard let lastSubview = scrollview.subviews.last else { return } // 用maxY直接获取最后一个子视图的底部位置,比origin.y+height更简洁 let totalContentHeight = lastSubview.frame.maxY scrollview.contentSize = CGSize(width: UIScreen.main.bounds.width, height: totalContentHeight) }如果是在UICollectionViewCell里,记得在cell的类里重写这个方法,或者在cell配置完成后调用
cell.layoutIfNeeded()再计算contentSize。关闭滚动视图的自动内边距调整
iOS 11+的contentInsetAdjustmentBehavior属性会让系统自动给滚动视图添加安全区域内边距,嵌套场景下很容易导致滚动范围计算混乱。你可以给cell里的UIScrollView加上这个设置:scrollview.contentInsetAdjustmentBehavior = .never同时也检查一下外层的UICollectionView,如果它的这个属性是默认的
.automatic,也可以改成.never,避免双重干扰。改用Auto Layout自动计算contentSize(推荐)
手动计算frame本质上就容易出错,尤其是多设备适配场景。如果你的scrollView子视图都是用Auto Layout约束的,完全可以让系统自动计算contentSize:- 给scrollView添加一个内部的contentView(UIView)
- 给contentView设置约束:top、bottom、leading、trailing都和scrollView的contentLayoutGuide对齐,同时设置contentView的width等于scrollView的frameLayoutGuide的width
- 把所有子视图放到contentView里,给子视图设置从上到下的连续约束(比如第一个子视图top到contentView top,最后一个子视图bottom到contentView bottom)
这样系统会自动根据子视图的总高度计算scrollView的contentSize,完全不需要手动设置,适配所有设备都没问题。
验证scrollView的实际可滚动范围
可以在运行时打印这两个值:print("contentSize height: \(scrollview.contentSize.height)") print("scrollView frame height: \(scrollview.frame.size.height)")如果contentSize高度是对的,但还是滚不到底,那大概率是scrollView本身的frame高度被安全区域截断了,这时候要检查scrollView和cell contentView的约束是否正确,确保scrollView的高度等于cell的高度(也就是屏幕高度)。
试试上面的方法,应该能解决你的问题!
内容的提问来源于stack exchange,提问作者726a

