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 来设置

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