iOS 10下如何模拟adjustedContentInset计算UITableView相对滚动量?
这个问题在iOS 10及更早版本确实很常见——系统自动调整滚动视图的偏移量时,首次scrollViewDidScroll(_:)调用会出现非预期的contentOffset跳变,导致我们无法直接用原始偏移量计算相对滚动距离。下面是具体的解决思路和代码实现:
核心思路
在iOS 10及以下版本,当UITableView处于UINavigationController内且automaticallyAdjustsScrollViewInsets为true时,系统会在视图布局完成后(viewDidLayoutSubviews阶段)自动给tableView设置contentInset.top,其值等于状态栏高度 + 导航栏高度。我们只需要提前获取这个初始的顶部偏移基准,再用实时的contentOffset.y减去它,就能得到和iOS 11中adjustedContentInset等效的相对滚动量。
完整代码实现
首先定义一个属性存储初始的顶部偏移基准:
private var initialTopOffset: CGFloat = 0.0
然后在viewDidLayoutSubviews中获取这个基准值(确保只获取一次,避免重复计算):
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() guard initialTopOffset == 0 else { return } if #available(iOS 11.0, *) { initialTopOffset = tableView.adjustedContentInset.top } else { // 分两种情况处理:系统自动调整或手动调整 if tableView.automaticallyAdjustsScrollViewInsets { initialTopOffset = tableView.contentInset.top } else { // 手动计算状态栏+导航栏高度 let statusBarHeight = UIApplication.shared.statusBarFrame.height let navBarHeight = navigationController?.navigationBar.frame.height ?? 0.0 initialTopOffset = statusBarHeight + navBarHeight } } }
最后在scrollViewDidScroll(_:)中计算相对滚动量:
override func scrollViewDidScroll(_ scrollView: UIScrollView) { let deltaY = scrollView.contentOffset.y - initialTopOffset // 这里使用deltaY来修改你的单元格子视图尺寸和位置即可 }
为什么这么做?
在iOS 10中,viewDidLoad阶段tableView的布局还未完成,系统还没来得及调整contentInset,所以此时contentOffset是CGPointZero;而到了首次scrollViewDidScroll(_:)调用时,系统已经完成了布局调整,contentOffset.y会跳至initialTopOffset的值,这就是你看到的非零偏移。用实时偏移量减去这个基准值后,deltaY会从0开始变化,完全符合我们预期的相对滚动逻辑。
内容的提问来源于stack exchange,提问作者Andy Obusek

