iOS 11适配:contentInsetAdjustmentB
之前写过一篇文章描述了下影响页面布局的几个属性,如今iOS 11出来后变化挺大的,在这里重新梳理下。
01.png可以看到在iOS 11
中,UIViewController的automaticallyAdjustsScrollViewInsets
属性被弃用了,系统推荐我们使用UIScrollView的contentInsetAdjustmentBehavior
属性替代之。关于这个属性,系统提供了四种行为模式:
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等)是否超过了安全区来进行调整,需要注意的有两点:
- 这个"调整"不再是直接修改
scrollView.contentSize
,而是scrollView.adjustedContentInset
- 调整的值将根据具体超出多少值来确定,但最大值不能超过安全区的相应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。然后你就发现莫名起妙内容就被下移了。
tableView.frame = CGRectMake(0, 80, self.view.short_width, self.view.short_height - 80)
tableView.backgroundColor = [UIColor orangeColor];
接下来我们再说回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, regardless of whether the scroll view is scrollable
其实文档已经说的很清楚了,它与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是第一个被添加的子视图