加深理解代替单纯记忆
要了解contentOffset的本质,需要一点UIView渲染过程的前置知识
UIView渲染过程
渲染需要两个步骤,光栅化 和 合成
Rasterization(光栅化)
- 对于每个UIView,光栅化的结果是生成一些渲染指令和一张表示该视图内容的图片
Composition(合成)
-
从UIWindow到整个视图层级中有多个视图,每个视图都会执行光栅化
-
将光栅化后的每一张图,绘制到一起,产生用户最终看到的效果
-
一个视图绘制和它的父视图绘制到一起时,如何决定位置呢?需要用到
frame和bounds,其背后的数学原理如下CompositedPosition.x = View.frame.origin.x - Superview.bounds.origin.x; CompositedPosition.y = View.frame.origin.y - Superview.bounds.origin.y; -
多数情况
bounds是(0, 0),所以此时最终位置就等于frame -
当bounds不是(0, 0)时,绘制的位置一定是不同的
contentOffset
理解contentOffset要依赖UIView渲染中合成过程的数学表达式
- 首先我们自己想下,如果要让视图里的内容可以滚动,最容易想到的直接改内容视图的frame.origin
- 但如果有多个子视图,修改每个视图的坐标会很麻烦
- 因为修改父视图的bounds的origin会导致绘制的所有子视图位置都发生改变,所以,要让内容滚动,那就得改bounds
- 其实修改contentOffset就是在修改scrollview.bounds;那么拖拽的时候,同时修改contentOffset,就产生了滚动效果(等等,这不足以让scrollview滚动,一定要看完后面一条)
- contentSize,表示滚动的范围;如果这个范围不如scrollview大,内容就不能滚动,即contentOffset不会被改
- 关于contentOffset的值理解
- 官方的解释不是很容易理解,由于其值是一个point坐标,那么我们需要知道是哪个点相对于哪个坐标系下的值
- 有了contentSize就容易解释了,想象这样两个矩形,scrollView和contentSize,那么contentOffset就是scrollView的原点以contentSize的原点为坐标系原点的值
- 在不考虑其他因素时,contentOffset的值有个范围
- min:(0, 0),即scrollView原点和contentSize原点重合。因为如果小于0,contentSize以外的内容就会进入视野,其实并没有什么内容,所以不能这样的操作
- max: contentSize.width - scrollView.width,道理和最小值一样
contentInset
- 道理上讲,contentInset的值是改变了contentOffset的取值范围,contentOffset一小节提到了不考虑其他因素时的取值范围,contentInset就是其他因素
- 具体怎么改变contentOffset范围的呢?我目前的经验是从属性名称上更容易理解该属性的作用。contentInset我理解为,在滚动的内容与scrollView边界之间添加一些空白,contentInset(比如top)值为正时,滚动内容不再是只能到达scrollView的原点,而是可以到达scrollView内部且距离边界有空白;为负值时,则滚动内容连scrollView原点也到不了,只能超出了scrollView边界显示;
- 既然contentInset(比如top值)为正时,滚动内容(contentSize)可以在scrollView内且是距离scrollView原点更远,那么contentOffset的y值就成了负值了。所以contentInset的值变化和contentOffset是相反的
- 设置contentInset并展示scrollView时,为什么看不出来内容有变化
- 创建一个scrollView,为其设置了contentInset,scrollView的内容并没有因此发生滚动,只是contentOffset取值范围变化了
- 但为UITableView或UICollectionView设置contentInset时,当展示这两个视图时,能够看出视图发生了变化,也就是说contentOffset发生了变化
- 修改contentInset会改到contentOffset的取值范围,有时动态修改contentInset也会直接触发contentOffset修改。记住,不是所有时候都会改到contentOffset
- 该条内容是试验得出的结论,不保证一定正确。当新的contentInset的某一项值比当前contentInset该项值要小时,contentOffset会修改,其实就是内容会滚动
- 对于上面的试验结论,我举个具体例子
- viewDidLoad中,创建了UITableView(而不是UIScrollView,原因参见上段内容),并设置了contentInset的top是100
- 当在viewDidAppear中,将top值设置为80时,tableView是会滚动的,contentOffset会变化
- 若将top值改为120,则不会滚动
UIRefreshControl(contentInset的应用)
- 就是应用了contentInset的特点,将下拉刷新UI嵌入到scrollView中