挺有意思~
1. 使 UITableView 停止?jié)L動
你是不是想讓 tableView 滾到哪停在哪?如下:
[_tableView setContentOffset:_tableView.contentOffset animated:NO];
2. UITableView 滾動回調(diào)方法的執(zhí)行順序
你是不是對 tableView 拖拽滾動過程中的scrollViewDidEndDragging:赂乐、scrollViewDidEndDecelerating:兩個方法有疑惑紧索?如下:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
NSLog(@" ----scrollViewWillBeginDragging---->> ");
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
NSLog(@" ----scrollViewDidEndDragging---->> ");
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSLog(@" ----scrollViewDidEndDecelerating---->> ");
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(@" ----scrollViewDidScroll---->> ");
}
//// Log如下
2021-12-26 23:24:16.342277+0800 DJStoreModule_Example[84112:3027127] ----scrollViewWillBeginDragging---->>
2021-12-26 23:24:16.343020+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:16.359424+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:16.378549+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:16.403349+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:16.426958+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:16.446477+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:16.462370+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:16.488254+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:16.538023+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidEndDragging---->>
2021-12-26 23:24:24.314777+0800 DJStoreModule_Example[84112:3027127] ----scrollViewWillBeginDragging---->>
2021-12-26 23:24:24.316231+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:24.331232+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:24.362894+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:24.380299+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:24.400132+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:24.413904+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidEndDragging---->>
2021-12-26 23:24:24.414677+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:24.423152+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:24.439855+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
.
.
.
2021-12-26 23:24:26.072631+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:26.106503+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:26.140170+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidScroll---->>
2021-12-26 23:24:26.156451+0800 DJStoreModule_Example[84112:3027127] ----scrollViewDidEndDecelerating---->>
3. UITableView 手動拖拽時的滑動方向
有時我們需要判斷 tableView 的滾動是不是人為拖動的,人為觸發(fā)区赵、非人為觸發(fā)的滾動回調(diào)中惭缰,處理事情的方式不一。判斷方法如下:
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
//NSLog(@" ----willDisplayHeaderView---->> %ld", (long)section);
if (_dragType == StorePromotionDragTypeUp) {
// ...
}
}
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section {
//NSLog(@" ----willDisplayFooterView---->> %ld", (long)section);
if (_dragType == StorePromotionDragTypeDown) {
// ...
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
_beginContentOffset = scrollView.contentOffset.y;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
_dragType = StorePromotionDragTypeNone;
_beginContentOffset = MAXFLOAT;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (_beginContentOffset != MAXFLOAT) {
if (scrollView.contentOffset.y < _beginContentOffset ) {
_dragType = StorePromotionDragTypeDown;
} else if (scrollView.contentOffset.y > _beginContentOffset ) {
_dragType = StorePromotionDragTypeUp;
} else {
_dragType = StorePromotionDragTypeNone;
}
// 只有手動拖動時笼才,做一些事情...
if (_dragType != StorePromotionDragTypeNone) {
// ...
}
}
}
很簡單漱受,對不對?需要初始化一些參數(shù)哦骡送,一看便知...
4. 關(guān)于 UITableView 的 reloadData 方法
有時候需要在 tableView reloadData之后做一些操作昂羡,但是又依賴于reloadData之后的一些狀態(tài),那么就可能有問題了摔踱,因為reloadData內(nèi)部是異步執(zhí)行但是又沒有執(zhí)行完成的回調(diào)虐先。這樣就無法確定
tableView reloadData之后的狀態(tài),如尺寸派敷、contentSize和contentOffset等蛹批。
調(diào)用reloadData后,回調(diào)的執(zhí)行順序:
@IBAction func refresh(_ sender: Any) {
array.removeLast()
print("Reload Begin")
self.tableView.reloadData()
print("Reload End")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("numberOfRowsInSection")
return array.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("cellForRowAt")
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
cell?.textLabel?.text = array[indexPath.row]
return cell!
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
print("heightForRow")
return 44;
}
//* Log如下 */
// Reload Begin
// numberOfRowsInSection
// Reload End
// cellForRowAt
// heightForRow
// ...
目前有兩種解決辦法:
//// 1.通過layoutIfNeeded方法篮愉,強制重繪并等待完成腐芍。
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
// 執(zhí)行后續(xù)代碼...
//// 2.reloadData方法會在主線程執(zhí)行,通過GCD使后續(xù)操作排隊在reloadData后面執(zhí)行
[self.tableView reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
// 執(zhí)行后續(xù)代碼...
});
5. 優(yōu)雅的在 tableView 任意位置更新數(shù)據(jù)
當(dāng)我們實時往UITableView
中插入數(shù)據(jù)并刷新列表的時候潜支,會發(fā)現(xiàn)列表是有抖動的甸赃。尤其是往當(dāng)前可見section之前的位置插入數(shù)據(jù)后立即執(zhí)行reload時,可見cell會下沉被”擠下去“冗酿。下沉的原因就需要理解關(guān)于ContentOffset埠对、ContentSize络断、ContentInset的區(qū)別,可以參考這里项玛。首先我們要知道ReloadData的一個特性:
When you call this method, the collection view discards any currently visible items and views and redisplays them. For efficiency, the collection view displays only the items and supplementary views that are visible after reloading the data. If the collection view’s size changes as a result of reloading the data, the collection view adjusts its scrolling offsets accordingly.
也就是說 reloadData 只刷新當(dāng)前屏幕可見的cells貌笨,只會對visibleCells調(diào)用
tableView:cellForRowAtIndexPath:
。而其contentOffset是保持不變的襟沮,所以我們才看到了“抖動現(xiàn)象”锥惋,就像之前的cell被擠下去了。
圖中灰色部分表示iPhone的屏幕开伏,粉紅色表示所有數(shù)據(jù)的布局大小膀跌,白色單元是隱藏在屏幕上方的數(shù)據(jù),綠色表示目標(biāo)廣告單于格固灵。
假設(shè)已經(jīng)確定數(shù)據(jù)會在上方插入捅伤,解決當(dāng)前可見 cell 抖動問題的方法如下:
//// 1. 以 cell 的粒度,重新錨定位置
//(需要在更新reload之前保存toIndexPath巫玻,在reload之后調(diào)用scrollToRowAtIndexPath:atScrollPosition:)
NSIndexPath *toIndexPath = [NSIndexPath indexPathForRow:destRow inSection: destSection];
[_tableView scrollToRowAtIndexPath:toIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
//// 2. 以像素級力度丛忆,重新錨定位置,可以保持界面順滑或巋然不動
//(需要在更新reload之后計算deltaOffset仍秤,并設(shè)置_tableView .contentOffset =.....)
- (void)reloadDataArr:(NSArray *)dataArr {
[_tableView reloadData];
CGFloat deltaOffset = dataArr.count * cellHeight;
CGPoint contentOffet = [self.tableView contentOffset];
contentOffet.y += deltaOffset;
self.tableView .contentOffset = contentOffet;
}
需要注意的是熄诡,如果cell高度動態(tài)變化的,就需要逐個累計cell高度來計算deltaOffset诗力。
參考文章:
http://www.reibang.com/p/1865cdf56770
http://www.reibang.com/p/4ffdd772c864
http://www.reibang.com/p/4271c6238b41
感謝凰浮!