復(fù)雜業(yè)務(wù)中的 tableView 處理

挺有意思~

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被擠下去了。

UITableView滾動的原理

圖中灰色部分表示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

感謝凰浮!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市姜骡,隨后出現(xiàn)的幾起案子导坟,更是在濱河造成了極大的恐慌屿良,老刑警劉巖圈澈,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異尘惧,居然都是意外死亡康栈,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門喷橙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啥么,“玉大人,你說我怎么就攤上這事贰逾⌒伲” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵疙剑,是天一觀的道長氯迂。 經(jīng)常有香客問我践叠,道長,這世上最難降的妖魔是什么嚼蚀? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任禁灼,我火速辦了婚禮,結(jié)果婚禮上轿曙,老公的妹妹穿的比我還像新娘弄捕。我一直安慰自己,他們只是感情好导帝,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布守谓。 她就那樣靜靜地躺著,像睡著了一般您单。 火紅的嫁衣襯著肌膚如雪分飞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天睹限,我揣著相機與錄音譬猫,去河邊找鬼。 笑死羡疗,一個胖子當(dāng)著我的面吹牛染服,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播叨恨,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼柳刮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痒钝?” 一聲冷哼從身側(cè)響起秉颗,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎送矩,沒想到半個月后蚕甥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡栋荸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年菇怀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晌块。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡爱沟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出匆背,到底是詐尸還是另有隱情呼伸,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布钝尸,位于F島的核電站括享,受9級特大地震影響闽铐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奶浦,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一兄墅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧澳叉,春花似錦隙咸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓶殃,卻和暖如春充包,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背遥椿。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工基矮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冠场。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓家浇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親碴裙。 傳聞我的和親對象是個殘疾皇子钢悲,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容