UIScrollView 的偏移问题
被控制的 UIScrollView
在 UIViewController 中有个属性:automaticallyAdjustsScrollViewInsets
,这个属性是用来控制 UIScrollView 的偏移行为的。
The default value of this property is true
, which lets container view controllers know that they should adjust the scroll view insets of this view controller’s view to account for screen areas consumed by a status bar, search bar, navigation bar, toolbar, or tab bar. Set this property tofalse
if your view controller implementation manages its own scroll view inset adjustments.
官方文档的意思当在 UIViewController 上添加 UIScrollView 的时候,会根据当前页面的 status bar、 search bar、navigation bar、toolbar 或 tab bar 来修改 UIScrollView 的内容区域。但是这个阶段的 UIViewController 比较蠢,不管任何情况下都会修改 UIScrollView 的偏移量。
比如我们现在有个 UINavigationController,然后添加一个 UIScrollView,然后在 UIScrollView 上面添加一个红色的方块,代码如下:
1 | let scrollView = UIScrollView() |

当我们把 UIScrollView 的 topAnchor 修改为跟 UIViewController 的 topLayoutGuide 发生约束:
1 | scrollView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true |

我们发现最终的效果是 UIScrollView 也发生了偏移,而且这个偏移是根据你顶部的 status bar 和 navigation bar 的高度来决定的。所以在 iOS 10 及以下的版本的时候,添加到 UIViewController 的 UIScrollView 总是会发生偏移。但是你可以通过把刚才说的那个属性 automaticallyAdjustsScrollViewInsets
设置成 false,UIViewController 就不会让你的 UIScrollView 发生偏移。但是这个属性会影响到所有添加到 UIViewController 上的 UIScrollView,如果有些想要发生偏移,有些不想发生偏移的时候就需要把 automaticallyAdjustsScrollViewInsets
设置成 false,然后通过代码单独去为每个 UIScrollView 设置不同的 contentInset。
这种被控制的生活很不是滋味,于是随着 iOS 系统来到 11 之后,UIScrollView 终于夺回了自己的偏移控制权。UIViewController 的automaticallyAdjustsScrollViewInsets
终于被废弃了,取而代之的是 UIScrollView 自己的contentInsetAdjustmentBehavior
。
自由的 UIScrollView
This property specifies how the safe area insets are used to modify the content area of the scroll view. The default value of this property is UIScrollView.ContentInsetAdjustmentBehavior.automatic.
UIScrollView 的contentInsetAdjustmentBehavior
的默认行为是automatic
,这和 iOS 10 默认行为的最大区别就是它会判断 UIScrollView 是被添加到哪个位置,然后根据这个位置来判断是否需要修改 UIScrollView 的偏移量。
还是拿上面图二的情况来讲,在 iOS 11 及 iOS 11 之后,我们还是照样只修改 UIScrollView 的 topAnchor:
1 | scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true |

此时我们发现 UIScrollView 并没有发生偏移,这也是因为 iOS 11 之后引入来 safeArea 的概念之后带来的 UI 方面的优化。
contentInsetAdjustmentBehavior
还有两个值,其中always
对应了automaticallyAdjustsScrollViewInsets
的true
,never
对应了automaticallyAdjustsScrollViewInsets
的false
。
至于scrollableAxes
,它其实就是根据 UIScrollView 的滚动方向来决定在哪个轴上使用 sa feArea。
通过contentInsetAdjustmentBehavior
我们就可以为 UIViewController 上的每一个 UIScrollView 定制它们的偏移行为。