tableView下拉刷新顫抖bug
1. 用MJRefresh下拉刷新UIcollectionview 左右顫抖問題
- 同學(xué)問我了一個問題,用MJRefresh下拉刷新UIcollectionview,效果如圖:

圖1

圖2

圖3

圖4

圖5
由于他設(shè)置的
self.UIcollectionview.contentInset = UIEdgeInsetsMake(20, 18, 30,-18);
設(shè)置了之后就出現(xiàn)這個問題。如果不設(shè)置這句話就沒有這個問題,但是跟他們UI給的效果圖就不一樣了。
- 看了一下MJRefresh的源碼:
3.0.8以前的版本:MJRefreshHeader.m中的122行處:
} else if (state == MJRefreshStateRefreshing) {
[UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
// 增加滾動區(qū)域
CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
self.scrollView.mj_insetT = top;
// 設(shè)置滾動位置
self.scrollView.mj_offsetY = - top;
} completion:^(BOOL finished) {
[self executeRefreshingCallback];
}];
}
下拉刷新中給collection的offsetY重新設(shè)置了一下。
3.0.8以后的版本:MJRefreshHeader.m中的126行處:之所以改成下面這樣是為了解決刷新上下顫抖的問題把【下面的2和3模塊有解釋】
else if (state == MJRefreshStateRefreshing) {
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
// 增加滾動區(qū)域top
self.scrollView.mj_insetT = top;
// 設(shè)置滾動位置
[self.scrollView setContentOffset:CGPointMake(0, -top) animated:NO];
} completion:^(BOOL finished) {
[self executeRefreshingCallback];
}];
});
給我同學(xué)的問題的解決方法:修改框架
else if (state == MJRefreshStateRefreshing) {
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
// 增加滾動區(qū)域top
self.scrollView.mj_insetT = top;
// 判斷了一下 這里面
if ([self.scrollView isKindOfClass:[UICollectionView class]]) {
self.scrollView.mj_offsetY = - top;
}else {
[self.scrollView setContentOffset:CGPointMake(0, -top) animated:NO];
}
} completion:^(BOOL finished) {
[self executeRefreshingCallback];
}];
});
2. 解決上拉加載更多,tableview抖動問題
【問題描述】在一個自己實(shí)現(xiàn)加載更多的App中,當(dāng)上拉操作的時候,從網(wǎng)絡(luò)端下載下來數(shù)據(jù),并更新tableview,打?。喝缦?/p>
2016-04-01 19:04:16.078 [2044:865035] contentInset:{64, 0, 56, 0}
2016-04-01 19:04:16.079 [2044:865035] setContentOffset:{-0, -64}
2016-04-01 16:40:20.573 [1869:836104] contentInset:{64, 0, 172, 0}
2016-04-01 16:40:20.575 [1869:836104] setContentOffset:{0, 1441}
2016-04-01 16:40:20.789 [1869:836104] contentInset:{64, 0, 56, 0}
2016-04-01 16:40:20.792 [1869:836104] setContentOffset:{0, 1325}
從上面的log中可以看到,tableView初始化后,contentInset.bottom是56 top是64(恰好是tabBar和NaviBar的高度)。
注意:
打印出現(xiàn)contentInset.bottom = 172,因?yàn)榇藭r“加載更多”的view加到了tableview的末尾,所以contentInset.bottom += “加載更多”的view.height ,最后即是172.
最后,contentInset.bottom又恢復(fù)為56,這是因?yàn)椤凹虞d更多”的view隱藏了,tableview的ContentInset又恢復(fù)了。
當(dāng)“加載更多”獲取數(shù)據(jù)下來,tableview更新后。由于contentInset從56—>172—>56。所以,會有一個抖動的現(xiàn)象:如下圖:

- 從loading開始,加載更多后,“悄巴蜀”這個cell出來了,但是tableview先向下滑動,在向上滑動,產(chǎn)生了抖動現(xiàn)象。
【原因】
當(dāng)loading的時候,contentInset.bottom是172,當(dāng)loading隱藏的時候contentInset.bottom = 56.這是因?yàn)樵趯ableview的contentInset賦值的時候,contentOffset也會相應(yīng)改變。contentOffset的變化導(dǎo)致了抖動。
下面的log展示了,contentInset和contentOffset相關(guān)聯(lián)的問題:
2015-04-01 16:40:20.573 [1869:836104] contentInset:{64, 0, 172, 0}
2015-04-01 16:40:20.575 [1869:836104] setContentOffset:{0, 1441}
2015-04-01 16:40:20.789 [1869:836104] contentInset:{64, 0, 56, 0}
2015-04-01 16:40:20.792 [1869:836104] setContentOffset:{0, 1325}
兩次contentOffset的差值116,正好是contentInset的差值。
【解決】
tableview的contentInset還是要恢復(fù)的,但是contentOffset達(dá)到1441的較高值后,后面1325就可以忽略了。這樣即可解決抖動
-
另外:當(dāng)loading失敗的時候,沒有新的cell進(jìn)來,會不會造成tableview末端懸空?
答案是:不會懸空;這是因?yàn)椋簾o論如何contentInset一定會恢復(fù)。既然contentInset已經(jīng)恢復(fù)為56了,那么即便contentOffset在較高位置,它也會自動滑下來。因?yàn)閏ontentInset限制了tableview一定要滑回來。類似與普通的tableview,用戶手動將tableview的尾部向上拉,松手后,tableview自動還原到原來位置。
在進(jìn)入IOS8之后,你有沒有注意到老式的下拉刷新可能會抖一下, 在下拉松開后,scrollView即將回到“刷新中…”的狀態(tài)過程中的時候。如果你有這個問題,那不妨跟隨我來看看怎么解決這個問題。
2. 解決下拉刷新,tableview抖動問題
- 我們先來看看在手松開之后我們對scrollView做了什么事情:
ScrollViewDidEndDragging => setContentInset:
為了保證在“Loading”的狀態(tài)下,下拉刷新控件可以展示,我們對contentInset做了修改,增加了inset的top. 那這樣一步操作為什么會導(dǎo)致scrollView抖動一下呢。如下
-
我在scrollViewDidScroll:中打了個斷點(diǎn),來看看在setContentInset:之后發(fā)生了什么事情。 我設(shè)置的inset.top = 64; 結(jié)果發(fā)現(xiàn)scrollView的contentOffset發(fā)生了這樣的變化:
(0, -64) => (0, -133) => (0, -64)
由以上數(shù)據(jù)可以看出,contentOffset在這個過程中先被向下移動了一段,再回歸正常。 猜測問題原因:和上面的原因是一樣的。
解決方法
網(wǎng)上的方法:async
- 第一思路是避開這個沖突,于是我把 setContentInset: 的方法異步調(diào)用一下:
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:kAnimationDuration animations:^{
self.scrollView.contentInset = inset;
} completion:^(BOOL finished) {
}];
});
不能解決
- 修改: 強(qiáng)設(shè)contentOffset
既然是因?yàn)閏ontentOffset改變導(dǎo)致的,我就再設(shè)置一下contentOffset應(yīng)該就行了
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:kAnimationDuration animations:^{
self.scrollView.contentInset = inset;
self.scrollView.contentOffset = CGPointMake(0, -inset.top);
} completion:^(BOOL finished) {
}];
});
沒用,問題還是存在
- 將setContentOffset: 方法改為 setConentOffset:animated: 。 問題就解決了!看來系統(tǒng)里面這兩個方法的實(shí)現(xiàn)是不同的啊。
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:kAnimationDuration animations:^{
self.scrollView.contentInset = inset;
[self.scrollView setContentOffset:CGPointMake(0, -inset.top) animated:NO];
} completion:^(BOOL finished) {
}];
});