一. 概述
一般項目里面用到UITableView的概率還是灰常大的, 我的項目從一開始也用了. 大概的來說就是類似一個收件箱的功能, 推送來一條消息就加一個cell, 這是很簡單的. 藍鵝, 接下來經歷了兩次需求更改:
1. 增加'數據本地化'的功能;
2. 增加查看歷史消息功能, 也就是下拉加載更多的消息.
第一個還好說, 就是用FMDB弄了個簡單的數據庫, 差不多就是獲取數據的途徑變成從數據庫獲取而已;
在增加了第一個需求之后, 疊加提出第二個需求, 看起來也很簡單哦, 不就是加數據嘛. 但是, 當我開始做起來的時候才發(fā)現, 坑蠻多的.
這篇文章是基于我自己項目所寫的, 可能在一些地方處理比較特殊并不具備"普世價值"; 但從開發(fā)者學習的角度來說, 我始終認為思路比代碼重要. 所以這里我可能在思路方面比較啰嗦點, 直接要代碼的慎重選擇啊.
先說一下, 需求二里面主要遇到的坑有兩個:
坑一 : 下拉加載的10條消息, 顯示直接在第0-4條, 要看第5-9條的話又得反方向上滑
為了解決坑一, 將tableview和cell都旋轉了180°, 這時候, 坑二來了
坑二 : 在初始cell數量較少的時候, 推送新消息過來增加cell的時候導致界面跳動
二. 項目過程
2.1需求分析
由于第一個需求太過簡單, 所以就忽略啦. 這里直接講第二個需求的.
剛剛上面講的不是很清楚, 這里補充一下第二個需求的具體:
1.進入到界面的時候, 里面要先顯示最多10條消息;
2.下拉可以加載歷史數據(每次10條);
3.當有新消息推送過來的時候, 要添加在最底部, 并滾動到最底部;
4.三日內做完.
剛看到這個需求的時候, 我的第一個反應是這太TM簡單了, too simple! 看老夫如何在三天時間以內用半天做完再用兩天半假裝沒做(tou)完(lan)????.
2.2 坑一
藍鵝, 忙活半天過后, 我發(fā)現自己too young too naive????. 這就是上面說的坑一, 圖是這樣的, 可以看到點擊"消息+1"的時候還算正常, 但是下拉加載更多消息(10條)的時候, 都是停留在最頂部, 也就是上面坑一說的, 要看第5-9條的時候, 得反方向上滑
為了解決坑一, 我嘗試過調試NSIndexPath并且使用scrollToRowAtIndexPath:<#(nonnull NSIndexPath *)#> atScrollPosition:<#(UITableViewScrollPosition)#> animated:<#(BOOL)#>]
方法來各種折騰....再過了半天之后????放棄了, 下班, 對, 下班!!!誰說程序猿都得加班的????.
2.3 坑二
昨晚想了一(yi)下(晚)和簡單的實踐(說好的不加班呢, 變相加班????!), 發(fā)現在uitableview的底部使用inset...和scroll...方法拼接數據很完美, 腦洞大開," 獨創(chuàng)"(后來發(fā)現網上也有)ios大法之乾坤大挪移----將整個tableview旋轉180°!!!!! 當然cell也要旋轉180°.
旋轉的代碼分別是:
CGAffineTransform transform =CGAffineTransformMakeRotation(M_PI);
[tbV setTransform:transform];
cell的旋轉:
CGAffineTransform transform =CGAffineTransformMakeRotation(M_PI);
[self.contentView setTransform:transform];
當然, 使用mjrefresh的話, 就得用原來使用的mj_header換成mj_footer了(關于mjrefresh的使用).
弄好之后是這樣的, cell直接在最底部:
這明顯是不行的, 為了使得cell數量比較少的時候可以頂置(實際是底置, 因為tableview旋轉了180°了), 于是想到了根據tableView.contentSize.height
(關于 tableView.contentSize你想了解的)來設置tableView.contentInset
, 代碼是這樣的
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
if (tableView.contentSize.height < tableView.frame.size.height)
{
tableView.contentInset = UIEdgeInsetsMake(tableView.frame.size.height - tableView.contentSize.height - RSRealValue(120), 0, 0, 0);
}
}
運行出來一看, 哇, 完美解決!!!! 藍鵝, 高興地太早了????. 這時候下拉加載時沒問題的, 但新加一條消息的時候, 新的cell會把原來的cell頂上去, 而不是拼接在最下面, 如圖:
經過一番思考, 本人大致的認為, 在新加入cell的時候, 原來的tableView.contentSize還是沒變, 也就是說tableView.contentSize的布局其實還是只到了上一個cell的底部, 并以此往復, 所以出現了上圖里面加了3條新消息再下滑的時候才出現????.
這時候, 又想到了一個方法, 可以嘗試監(jiān)聽tableView.contentSize還動態(tài)修改tableView.contentSize.....&%$#@!@#$%&%$過程省略幾百字, 直接說結果吧, 然并卵. 而且, 在ios的UITableView里面, 并不是很推薦修改tableView.contentSize, 因為這個是蘋果自身維持動態(tài)平衡的, 我們強心修改的話會影響其平衡, 而且在控制臺會出現警報. 所以很不推薦.
就這樣又折騰了半天.....下午整個人都在想這個問題. 終于在吃了一顆糖果之后, 想到一個方法:
為啥不通過記錄cell的總高度來代替tableView.contentSize, 然后設置一個透明的tableheaderview來代替設置tableView.contentInset??????快被自己的機智感動了????.
這樣既可以繞過上面說的問題了. 說干就干.
需要在cell的model文件里面增加一個記錄本cell高度的屬性:
// 計算出來的cell的高度
@property (nonatomic, assign) CGFloat cellHeight;
同時, 也得在控制器文件里面增加一個記錄所有cell總高度的屬性:
// cell的疊加高度
@property (nonatomic, assign) CGFloat cellsTotalHeight;
并且寫了它的懶加載方法, 這樣每次在獲取總高度的時候都能保證是最新的
- (CGFloat)cellsTotalHeight{
CGFloat heightAll = 0;
for (int i = 0; i < self.dataMulArr.count; i ++) {
OneModel *model = self.dataMulArr[i];
heightAll = heightAll + model.cellHeight;
}
_cellsTotalHeight = heightAll;
NSLog(@"cell總高度: %f",heightAll);
return heightAll;
}
同時, 返回頭部視圖和高度是這樣de(為了方便調試, 先把頭部視圖設置成有個半透明色)
#pragma mark- 返回頭部視圖
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
UIView *headV = [[UIView alloc] init];
// headV.backgroundColor = [UIColor clearColor];
headV.backgroundColor = RSColorFromRGBA(0x00FF00, 0.5);
return headV;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
NSLog(@"頭部視圖高度中cell: %f", self.cellsTotalHeight);
CGFloat headHeight = 0;
if (self.cellsTotalHeight < self.tbV.frame.size.height)
{
headHeight = self.tbV.frame.size.height - self.cellsTotalHeight - RSRealValue(240);
}
return headHeight;
}
跑出來是這樣的:
這時候再把tableheaderview的背景色改成透明的, 就成了這樣了:
果然完美解決了????, 這時候其實只剩一天時間假裝沒完(偷)成(lan)了.
不過還是要注意一下, 看圖倒過來加header.gif
將tableheaderview改成透明之后, 新增cell還是會被tableheaderview擋住, 所以最后一個cell是點擊不到的. 因為我項目里面的cell不需要點擊, 所以這個問題我可以忽略; 如果小伙伴項目里面的cell需要點擊的話, 就要將tableheaderview高度進一步減去新增cell的高度哦. 這里我就不做具體代碼了, 小伙伴們自己探索一下把.
三. 小總結
總結起來, 這里面幾個關鍵點是:
1.旋轉180°
2.不使用reloaddata方法, 而是使用inser....和scroll...兩個方法
3.通過增加tableheaderview來實現倒轉tableview之后cell的頂置, 并且動態(tài)的改變tableheaderview的高度.
這個雖然只是項目的一小部分, 但每次解決問題之后都能獲得成就感, 這就是碼農單純的快樂吧.