iOS 11适配:contentInsetAdjustmentBehavior解析

上一篇一篇文章介绍了影响页面布局的几个属性,如今iOS 11出来后变化挺大的,在这里重新梳理下。

可以看到在iOS 11中,UIViewController的automaticallyAdjustsScrollViewInsets属性被弃用了,系统推荐我们使用UIScrollView的contentInsetAdjustmentBehavior属性替代之。关于这个属性,系统提供了四种行为模式:

1
2
3
4
UIScrollViewContentInsetAdjustmentAutomatic
UIScrollViewContentInsetAdjustmentScrollableAxes
UIScrollViewContentInsetAdjustmentNever
UIScrollViewContentInsetAdjustmentAlways

第3、4种看起来比较清晰,要么不调整,要么”一直”调整。但估计不少朋友看到第1、2种会一脸懵逼,包括第4种里“一直”这个词。在这解释之前我们先分析下为什么automaticallyAdjustsScrollViewInsets会被弃用。

在我看来原因可能之前使用automaticallyAdjustsScrollViewInsets的方案太单调粗暴了。当处于 ①(文末末尾)情况时的scrollView,系统会自动修改其contentInset属性,举个例子:如果存在状态栏和导航栏,则contentInset的top值则会被修改为64,内容自动下移64。当底部存在系统UITabBar时,则bottom值修改为49,即下方额外增加49的滚动距离。

我们知道iOS 11后引入了安全区的概念safeAreaInsets。

以不带UITabBar的iPhone X为例:NavigationController的rootViewController.view(以下用self代称该rootViewController),其safeAreaInsets为{88, 0, 34, 0},在contentInsetAdjustmentBehavior属性出现之前,系统是根据①情况进行的调整。而现在,系统将会根据ScrollView视图大小(包括其类族UITableView等)是否超过了安全区来进行调整,需要注意的有两点:

  1. 这个”调整”不再是直接修改scrollView.contentSize,而是scrollView.adjustedContentInset

  2. 调整的值将根据具体超出多少值来确定,但最大值不能超过安全区的相应EdgeInsets方向的值。以self.view的safeAreaInsets为{88, 0, 34, 0}为例。此时添加一个tableView,其高度为self.view.short_height + 25,那么tableView.adjustedContentInset的bottom则为25。但如果超出高度为134,bottom最高也只会是34,这样就会由于表格高度超过屏幕100,而出现”拉不到底部”的情况。

第二点的效果相比以前的automaticallyAdjustsScrollViewInsets方案,可以说智能很多,因为当处于①情况时,哪怕你的scrollView的布局位置根本就没被导航栏挡住,它都会给你调整64。然后你就发现莫名起妙内容就被下移了。

接下来我们再说回contentInsetAdjustmentBehavior属性的这四个值:

首先是UIScrollViewContentInsetAdjustmentNever,如名所示:就算你的ScrollView超出了safeAreaInsets,系统不会对你的scrollView.adjustedContentInset做任何事情,即不作任何调整;

UIScrollViewContentInsetAdjustmentAlways: 只要超了安全区,就调整相应的超出值,调整的最大值不会超过安全区相应EdgeInsets方向的最大值,如刚刚上述第2点;

UIScrollViewContentInsetAdjustmentScrollableAxes:系统会根据ScrollView的滚动方向来进行判断,假设我只是一个横向滚动的ScrollView,那即便我的布局起点和高度值超过了self.view的安全区,那么系统也不会调整scrollView.adjustedContentInset对应的top与bottom方向值,只可垂直方向滚动同理,直接设置scrollView.scrollEnabled = NO也同理;

UIScrollViewContentInsetAdjustmentAutomatic:系统默认值。文档上是这样说的:

Similar to .scrollableAxes, but for backward compatibility will also adjust the top & bottom contentInset when the scroll view is owned by a view controller with automaticallyAdjustsScrollViewInsets = YES inside a navigation controller,

其实文档已经说的很清楚了,它与UIScrollViewContentInsetAdjustmentScrollableAxes行为相似,但是为了兼容以前①这种情况,即使scrollView是不可滚动,也会根据safeAreaInsets超出范围进行调整。(具体效果可以试着自己上手调试,这里就不贴代码和示意图了)。

关于刚才说的注意点二,我想补充的是,当出现表格过高(比如超出了100)而导致”拉不到底部”的情况时,你可以选择额外设置tableView.contentInset属性,bottom方向设为100,或者选择修改self.additionalSafeAreaInsets的属性。前者影响tableView.adjustedContentInset值,后者影响self.view.safeAreaInsets。你会发现刚好是两数之和,事实上adjustedContentInset的值正是由contentSize加上contentInsetAdjustmentBehavior所调整的值。而self.view.safeAreaInsets会在原来的基础上,加上你的additionalSafeAreaInsets。由于我们很少直接修改contentSize,所以基本上tableView.adjustedContentInset都是系统的调整值。

其实,苹果可能本意是方便开发者,但事实上如果你对这些属性的来龙去脉不太了解清楚的话,确实是挺不方便的。。。因此可能现在很多的人做法都是一开始就设置之前的automaticallyAdjustsScrollViewInsets为NO,设置新的contentInsetAdjustmentBehavior为UIScrollViewContentInsetAdjustmentNever。就我个人看来,如果你需要实现类似系统默认那种表格穿透半透明导航栏或者底部半透明TabBar的效果时,这个属性使用起来就很舒服,直接tableView.frame = self.view.bouds 或者make.edges.equal.to(self.view)就好了。而无需再设置额外的contentSize。如果不需要类似的穿透,那可以选择直接将其禁止。

① scroll view is owned by a view controller with automaticallyAdjustsScrollViewInsets = YES inside a navigation controller
PS:并且该scollView是第一个被添加的子视图

文章目录
|