iOS滚动视图下压上移全解析

最近在项目开发过程中遇到了iOS8下UITableView莫名向上或向下偏移64个像素的问题,有经验的人就会知道这是iOS7之后导航栏默认透明造成的,然而不仅仅是这样,下面我们就来一起探讨下这个问题。

影响视图偏移的属性

translucent(UINavigationBar)

导航栏的是否透明,iOS7之后默认为YES,当translucent为YES(导航栏透明)时,滚动视图位置正常,当translucent为NO(导航栏不透明)时,滚动视图位置下移64个像素。

automaticallyAdjustsScrollViewInsets(UIViewController)

该属性仅在translucent为YES时有效,意为是否自动调整滚动视图内容偏移,默认是YES,当automaticallyAdjustsScrollViewInsets为YES时自动将滚动视图内容下压64个像素,当automaticallyAdjustsScrollViewInsets为NO时则不下压滚动视图内容

extendedLayoutIncludesOpaqueBars(UIViewController)

该属性仅在translucent为NO时有效,意为在不透明导航栏情况下扩展布局,默认是NO,当extendedLayoutIncludesOpaqueBars为YES时,滚动视图忽略不透明导航栏,滚动视图位置正常,滚动视图内容下移64个像素,当extendedLayoutIncludesOpaqueBars为NO时,滚动视图不忽略导航栏的不透明,滚动视图位置下移64个像素,滚动视图内容正常(如果忽略背景的话,滚动视图内容的位置是一样的,但是实际上布局是不一样的,需注意)

所以滚动视图只有在translucent为NO且extendedLayoutIncludesOpaqueBars为NO时滚动视图位置会下移64个像素,其他情况都是正常的,只需根据情况调整视图内容偏移属性即可。

以下是所述几种情况的UI调试

translucent为YES,automaticallyAdjustsScrollViewInsets为YES时的视图分析
TYAY

translucent为YES,automaticallyAdjustsScrollViewInsets为NO时的视图分析
TYAN

translucent为NO,extendedLayoutIncludesOpaqueBars为NO时的视图分析
TNEN

translucent为NO,extendedLayoutIncludesOpaqueBars为YES时的视图分析
TNEY

滚动视图内容偏移的条件

想要以上属性定义的滚动视图内容下移64个像素有效需要该滚动视图在其父视图堆栈当中处于最底层,也就是说滚动视图下面不能有其他子视图

鉴于这个限制条件有一个取巧的方法,当不能改动上述属性又必须让滚动视图内容不下移的时候可以在滚动视图前面add一个填充视图,这个视图的大小和颜色等属性都没有限制,因此你可以设置frame很小而且透明,这样就轻松的解决了一个难题。

iOS 8 UITabBarController的遗留问题

UITabBarController下挂载的UIViewController的automaticallyAdjustsScrollViewInsets和extendedLayoutIncludesOpaqueBars无效,在translucent为YES时所有滚动视图位置正常,滚动视图内容向下偏移64个像素,translucent为NO时所有滚动视图位置下移64个像素,滚动视图内容位置正常。

但是在iOS 8系统当中,在translucent为YES时只会针对UITabBarController下第一个出现的视图进行滚动视图内容下压,这就造成了第一个出现的视图和其他视图的偏移显示不一致。针对这个问题,如果不能改动translucent为NO的话就需要用到我们第二部分提到的方法,我们需要在UITabBarController下的所有视图当中insert一个填充视图,让所有的滚动视图内容都不发生偏移,这个样就可以正常显示了。

补充:解决UIViewController的View下移导致的偏移

如果大家细心的话,可以发现我把UIViewController当中的View的颜色也设置成了红色。在translucent为NO、extendedLayoutIncludesOpaqueBars也为NO的时候发现View下移了64个像素(详情可以比较上面四种情况的UI调试)。如果是因为UIViewController的View下移导致视图偏移,我们可以

  1. 设置navigationBar的translucent为YES(工程整体风格,一般不允许修改)
  2. 设置extendedLayoutIncludesOpaqueBars为YES(只影响当前视图,推荐)

补充:iOS 11的相关更新

automaticallyAdjustsScrollViewInsets在iOS 11当中已经弃用且无效,需要使用UIScrollViewcontentInsetAdjustmentBehavior来设置

if (@available(iOS 11.0, *)) {
    _mWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}