iOS11 & iPhone X 上UIScrollView(UITableView)内容偏移问题

原因

  • iOS11中UIViewControllerautomaticallyAdjustsScrollViewInsets属性已经不再使用,我们需要使用UIScrollViewcontentInsetAdjustmentBehavior属性来替代它。
    UIScrollViewframe超出安全区域范围时,系统会自动调整UIScrollViewsafeAreaInsets值,于是会影响UIScrollViewadjustContentInset值,从而导致UIScrollView的内容发生偏移。iOS11 控制UIScrollView内容偏移量的属性是adjustContentInset,而adjustContentInset值是系统根据safeAreaInsetscontentInset计算得来的,计算方式由contentInsetAdjustmentBehavior决定。

  • 补充:有导航栏时safeAreaInsets值自动调整为(88,0,34,0)。没有导航栏时safeAreaInsets值为(44,0,34,0)。

安全区域

  • 安全区域是iOS 11新提出的。

  • 安全区域帮助我们将view放置在整个屏幕的可视的部分。即使把navigationBar设置为透明的,系统也认为安全区域是从navigationBarbottom开始,保证不被系统的状态栏、或导航栏覆盖。controller可以使用additionalSafeAreaInsets去扩展安全区域使它包括自定义的content在界面上。每个view都可以改变安全区域嵌入的大小,controller也可以。

  • safeAreaInsets属性反映了一个view距离该view的安全区域的边距。对于一个UIViewController的根视图而言,safeAreaInsets值包括了被statusbar和其他可视的bars覆盖的区域和其他通过additionalSafeAreaInsets自定义的insets值。view层次中的其它viewsafeAreaInsets值反映了该view被覆盖的部分。如果一个view全部在它父视图的安全区域内,则safeAreaInsets值为(0,0,0,0)。

adjustContentInset 属性的计算方式

首先看UIScrollView在iOS11新增的两个属性:adjustContentInsetcontentInsetAdjustmentBehavior

  1. UIScrollViewContentInsetAdjustmentAutomatic:如果scrollView在一个automaticallyAdjustsScrollViewContentInset = YESUIViewController上,并且这个UIViewController包含在一个UINavigationController中,这种情况下会设置在top & bottomadjustedContentInset = safeAreaInset + contentInset不管是否滚动。其他情况下与UIScrollViewContentInsetAdjustmentScrollableAxes相同。

  2. UIScrollViewContentInsetAdjustmentScrollableAxes: 在可滚动方向上adjustedContentInset = safeAreaInset + contentInset,在不可滚动方向上adjustedContentInset = contentInset;依赖于scrollEnabledalwaysBounceHorizontal / Vertical = YESscrollEnabled默认为Yes,所以大多数情况下,计算方式还是adjustedContentInset = safeAreaInset + contentInset

  3. UIScrollViewContentInsetAdjustmentNeveradjustedContentInset = contentInset 。当contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentNever的时候,adjustContentInset值不受safeAreaInset值的影响。

  4. UIScrollViewContentInsetAdjustmentAlwaysadjustedContentInset = safeAreaInset + contentInset

解决方法

  1. 重新设置tableViewcontentInset值,来抵消掉safeAreaInset值,因为adjustedContentInset = contentInset + safeAreaInset

  2. 设置tableViewcontentInsetAdjustmentBehavior属性为UIScrollViewContentInsetAdjustmentNever,这样adjustedContentInset = contentInset

    // 新API:`@available(iOS 11.0, *)` 可用来判断系统版本
    if ( @available(iOS 11.0, *) ) {
        self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    }
  1. 设置iOS11UIViewController新增属性addtionalSafeAreaInset
    iOS 11之前,大家是通过将UIViewControllerautomaticallyAdjustsScrollViewInsets属性设置为NO,来禁止系统调整tableViewcontentInset。如果还是想从controller级别解决问题,可以通过设置controlleradditionalSafeAreaInsets属性,因为当tableViewframe没有超出安全区域范围时,系统就不会调整tableViewsafeAreaInset值,从而也就不会发生内容偏移情况。

组头组尾高度

  • tableView的类型为UITableViewStyleGrouped时,系统默认tableView组头和组尾是有间距的,如果不需要这个间距的话,iOS11之前可以通过实现代理方法heightForHeaderInSection / heightForFooterInSection(返回一个较小值:0.01)来解决的。

  • iOS11之后不仅要实现代理方法heightForHeaderInSection / heightForFooterInSection,还要实现代理方法viewForHeaderInSection / viewForFooterInSection才能去掉间距。或者添加以下代码关闭高度估算,问题也能解决。

self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;

你可能感兴趣的:(iOS11 & iPhone X 上UIScrollView(UITableView)内容偏移问题)