本文章主要研究:scrollView的几个属性contentSize、contentOffset和contentInset的关系。并提供解决拉加载更多,tableview抖动问题的方案 。
相关文章: UITableview Deceleration 加速滑动(惯性滑动)、弹性回归原理
概念
1. contentSize 是scrollview可以滚动的区域,比如frame = (0 ,0 ,320 ,480) contentSize = (320 ,960),代表你的scrollview可以上下滚动,滚动区域为frame大小的两倍。
2. contentOffset 是scrollview当前显示区域顶点相对于frame顶点的偏移量(向屏幕内拉,偏移量是负值。向屏幕外推,偏移量是正数),比如上个例子,从初始状态向下拉50像素,contentoffset就是(0 ,-50),从初始状态向上推tableview100像素,contentOffset就是(0 ,100)。
3. contentInset 是scrollview的contentview的顶点相对于scrollview的位置,例如你的contentInset = (0 ,100),那么你的contentview就是从scrollview的(0 ,100)开始显示.
UIEdgeInsets的四个属性,如下:
typedef struct UIEdgeInsets {
CGFloat top, left, bottom, right; // specify amount to inset (positive) for each of the edges. values can be negative to 'outset'
} UIEdgeInsets;
分别代表距离上,左,下,右边的像素长度。
下面举个iPad例子:
(图1)
tableview初始化后,如上图所示,并打印了各个属性的值:
2015-04-01 19:43:00.501 xxx[2058:871144] contentInset:{64, 0, 56, 0}
2015-04-01 19:43:00.502 xxx[2058:871144] contentOffset:{-0, -64}
2015-04-01 19:43:00.503 xxx[2058:871144] contentSize{1024, 2037}
2015-04-01 19:43:00.503 xxx[2058:871144] frame{{0, 0}, {1024, 768}}
2015-04-01 19:43:00.504 xxx[2058:871144] bounds{{-0, -64}, {1024, 768}}
● 首先从contentInset分析,top=64,即NaviBar的高度,意思是从这儿开始显示tableview,而不是从(0,0)开始显示。
● 那么contentOffset = -64也就不难理解了:当前显示区域顶点相对于frame的偏移量,即-64。你可以这么理解:它等同于从(0,0)下拉到了y = 64的位置。
● 最后contentSize,宽度是iPad的屏幕宽度,而高度2037 = (cell.count 乘以 cell.height)。
● 另外两个属性frame是提前设定的pad的屏幕长和宽,bounds则是{{-0, -64}, {1024, 768}}
设置contentInset的好处
有要求如下:tableview显示的区域是NaviBar和tabBar中间的区域,且tableView滑动通过NaivBar和tabBar的时候,仍可以透过背景显示,故设置contentInset可以达到此效果。如图:
(图2)
上图描述了两个场景:
1 初始化的时候,tableview顶头是NaviBar的下沿。
2. 向上推的时候,通过NaviBar的背景可以看到tableView。
仅仅设置tableview的frame是无法达到这个效果的。
解决上拉加载更多,tableview抖动问题
【问题描述】在一个自己实现加载更多的App中,当上拉操作的时候,从网络端下载下来数据,并更新tableview,如图loading所示:
(图3,上拉刷新,loading)
2015-04-01 19:04:16.078 xxx[2044:865035] contentInset:{64, 0, 56, 0}
2015-04-01 19:04:16.079 xxx[2044:865035] setContentOffset:{-0, -64}
2015-04-01 16:40:20.573 xxx[1869:836104] contentInset:{64, 0, 172, 0}
2015-04-01 16:40:20.575 xxx[1869:836104] setContentOffset:{0, 1441}
2015-04-01 16:40:20.789 xxx[1869:836104] contentInset:{64, 0, 56, 0}
2015-04-01 16:40:20.792 xxx[1869:836104] setContentOffset:{0, 1325}
从上面的log中可以看到,tableView初始化后,contentInset.bottom是56 top是64(恰好是tabBar和NaviBar的高度)。
● 然后出现contentInset.bottom = 172,因为此时“加载更多”的view加到了tableview的末尾,所以contentInset.bottom += “加载更多”的view.height ,最后即是172.
● 最后,contentInset.bottom又恢复为56,这是因为“加载更多”的view隐藏了,tableview的ContentInset又恢复了,效果如上图3。
当“加载更多”获取数据下来,tableview更新后。由于contentInset从56—>172—>56。所以,会有一个抖动的现象:如下图4:
(图4)
从loading开始,加载更多后,“悄巴蜀”这个cell出来了,但是tableview先向下滑动,在向上滑动,产生了抖动现象。
【原因】当loading的时候,contentInset.bottom是172,当loading隐藏的时候contentInset.bottom = 56.这是因为在对tableview的contentInset赋值的时候,contentOffset也会相应改变。contentOffset的变化导致了抖动。
下面的log展示了,contentInset和contentOffset相关联的问题:
2015-04-01 16:40:20.573 xxx[1869:836104] contentInset:{64, 0, 172, 0}
2015-04-01 16:40:20.575 xxx[1869:836104] setContentOffset:{0, 1441}
2015-04-01 16:40:20.789 xxx[1869:836104] contentInset:{64, 0, 56, 0}
2015-04-01 16:40:20.792 xxx[1869:836104] setContentOffset:{0, 1325}
两次contentOffset的差值116,正好是contentInset的差值。
【解决】tableview的contentInset还是要恢复的,但是contentOffset达到1441的较高值后,后面1325就可以忽略了。这样即可解决抖动
另外:当loading失败的时候,没有新的cell进来,会不会造成tableview末端悬空?
答案是:不会悬空;这是因为:无论如何contentInset一定会恢复。既然contentInset已经恢复为56了,那么即便contentOffset在较高位置,它也会自动滑下来。因为contentInset限制了tableview一定要滑回来。类似与普通的tableview,用户手动将tableview的尾部向上拉,松手后,tableview自动还原到原来位置。
(完)
测试图片来自 http://www.th7.cn/Program/IOS/201412/326493.shtml
部分内容摘自:http://www.cnblogs.com/pengyingh/articles/2346128.html