姓名:唐來(lái)賓? 學(xué)號(hào):17101223417
轉(zhuǎn)載http://mp.weixin.qq.com/s/J0DKug2QSIaMguMTEkSrXQ
【嵌牛導(dǎo)讀】一般項(xiàng)目里面用到UITableView的概率還是灰常大的, 我的項(xiàng)目從一開(kāi)始也用了. 大概的來(lái)說(shuō)就是類似一個(gè)收件箱的功能, 推送來(lái)一條消息就加一個(gè)cell, 這是很簡(jiǎn)單的.
【嵌牛鼻子】數(shù)據(jù)插入,旋轉(zhuǎn)
【嵌牛提問(wèn)】如何簡(jiǎn)化數(shù)據(jù)插入過(guò)程?
【嵌牛正文】一. 概述
一般項(xiàng)目里面用到UITableView的概率還是灰常大的, 我的項(xiàng)目從一開(kāi)始也用了. 大概的來(lái)說(shuō)就是類似一個(gè)收件箱的功能, 推送來(lái)一條消息就加一個(gè)cell, 這是很簡(jiǎn)單的. 藍(lán)鵝, 接下來(lái)經(jīng)歷了兩次需求更改:
1. 增加'數(shù)據(jù)本地化'的功能;
2. 增加查看歷史消息功能, 也就是下拉加載更多的消息.
第一個(gè)還好說(shuō), 就是用FMDB弄了個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù), 差不多就是獲取數(shù)據(jù)的途徑變成從數(shù)據(jù)庫(kù)獲取而已;
在增加了第一個(gè)需求之后, 疊加提出第二個(gè)需求, 看起來(lái)也很簡(jiǎn)單哦, 不就是加數(shù)據(jù)嘛. 但是, 當(dāng)我開(kāi)始做起來(lái)的時(shí)候才發(fā)現(xiàn), 坑蠻多的.
這篇文章是基于我自己項(xiàng)目所寫(xiě)的, 可能在一些地方處理比較特殊并不具備"普世價(jià)值"; 但從開(kāi)發(fā)者學(xué)習(xí)的角度來(lái)說(shuō), 我始終認(rèn)為思路比代碼重要. 所以這里我可能在思路方面比較啰嗦點(diǎn), 直接要代碼的慎重選擇啊.
先說(shuō)一下, 需求二里面主要遇到的坑有兩個(gè):
坑一 : 下拉加載的10條消息, 顯示直接在第0-4條, 要看第5-9條的話又得反方向上滑
為了解決坑一, 將tableview和cell都旋轉(zhuǎn)了180°, 這時(shí)候, 坑二來(lái)了
坑二 : 在初始cell數(shù)量較少的時(shí)候, 推送新消息過(guò)來(lái)增加cell的時(shí)候?qū)е陆缑嫣鴦?dòng)
二. 項(xiàng)目過(guò)程
2.1需求分析
由于第一個(gè)需求太過(guò)簡(jiǎn)單, 所以就忽略啦. 這里直接講第二個(gè)需求的.
剛剛上面講的不是很清楚, 這里補(bǔ)充一下第二個(gè)需求的具體:
1.進(jìn)入到界面的時(shí)候, 里面要先顯示最多10條消息;
2.下拉可以加載歷史數(shù)據(jù)(每次10條);
3.當(dāng)有新消息推送過(guò)來(lái)的時(shí)候, 要添加在最底部, 并滾動(dòng)到最底部;
4.三日內(nèi)做完.
剛看到這個(gè)需求的時(shí)候, 我的第一個(gè)反應(yīng)是這太TM簡(jiǎn)單了, too simple! 看老夫如何在三天時(shí)間以內(nèi)用半天做完再用兩天半假裝沒(méi)做(tou)完(lan).
2.2 坑一
藍(lán)鵝, 忙活半天過(guò)后, 我發(fā)現(xiàn)自己too young too naive????. 這就是上面說(shuō)的坑一, 圖是這樣的, 可以看到點(diǎn)擊"消息+1"的時(shí)候還算正常, 但是下拉加載更多消息(10條)的時(shí)候, 都是停留在最頂部, 也就是上面坑一說(shuō)的, 要看第5-9條的時(shí)候, 得反方向上滑
正常的.gif
為了解決坑一, 我嘗試過(guò)調(diào)試NSIndexPath并且使用scrollToRowAtIndexPath:<#(nonnull nsindexpath=""> atScrollPosition:<#(uitableviewscrollposition)#> animated:<#(bool)#>]方法來(lái)各種折騰....再過(guò)了半天之后????放棄了, 下班, 對(duì), 下班!!!誰(shuí)說(shuō)程序猿都得加班的.
2.3 坑二
昨晚想了一(yi)下(晚)和簡(jiǎn)單的實(shí)踐(說(shuō)好的不加班呢, 變相加班????!), 發(fā)現(xiàn)在uitableview的底部使用inset...和scroll...方法拼接數(shù)據(jù)很完美, 腦洞大開(kāi)," 獨(dú)創(chuàng)"(后來(lái)發(fā)現(xiàn)網(wǎng)上也有)ios大法之乾坤大挪移----將整個(gè)tableview旋轉(zhuǎn)180°!!!!! 當(dāng)然cell也要旋轉(zhuǎn)180°.
旋轉(zhuǎn)的代碼分別是:
CGAffineTransform transform =CGAffineTransformMakeRotation(M_PI);
[tbV setTransform:transform];
cell的旋轉(zhuǎn):
CGAffineTransform transform =CGAffineTransformMakeRotation(M_PI);
[self.contentView setTransform:transform];
當(dāng)然, 使用mjrefresh的話, 就得用原來(lái)使用的mj_header換成mj_footer了(關(guān)于mjrefresh的使用).
弄好之后是這樣的, cell直接在最底部:
倒過(guò)來(lái)一.gif
這明顯是不行的, 為了使得cell數(shù)量比較少的時(shí)候可以頂置(實(shí)際是底置, 因?yàn)閠ableview旋轉(zhuǎn)了180°了), 于是想到了根據(jù)tableView.contentSize.height(關(guān)于 tableView.contentSize你想了解的)來(lái)設(shè)置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);
}
}
運(yùn)行出來(lái)一看, 哇, 完美解決!!!! 藍(lán)鵝, 高興地太早了. 這時(shí)候下拉加載時(shí)沒(méi)問(wèn)題的, 但新加一條消息的時(shí)候, 新的cell會(huì)把原來(lái)的cell頂上去, 而不是拼接在最下面, 如圖:
倒過(guò)來(lái)contentInset.gif
經(jīng)過(guò)一番思考, 本人大致的認(rèn)為, 在新加入cell的時(shí)候, 原來(lái)的tableView.contentSize還是沒(méi)變, 也就是說(shuō)tableView.contentSize的布局其實(shí)還是只到了上一個(gè)cell的底部, 并以此往復(fù), 所以出現(xiàn)了上圖里面加了3條新消息再下滑的時(shí)候才出現(xiàn).
這時(shí)候, 又想到了一個(gè)方法, 可以嘗試監(jiān)聽(tīng)tableView.contentSize還動(dòng)態(tài)修改tableView.contentSize.....&%$#@!@#$%&%$過(guò)程省略幾百字, 直接說(shuō)結(jié)果吧, 然并卵. 而且, 在ios的UITableView里面, 并不是很推薦修改tableView.contentSize, 因?yàn)檫@個(gè)是蘋(píng)果自身維持動(dòng)態(tài)平衡的, 我們強(qiáng)心修改的話會(huì)影響其平衡, 而且在控制臺(tái)會(huì)出現(xiàn)警報(bào). 所以很不推薦.
就這樣又折騰了半天.....下午整個(gè)人都在想這個(gè)問(wèn)題. 終于在吃了一顆糖果之后, 想到一個(gè)方法:
為啥不通過(guò)記錄cell的總高度來(lái)代替tableView.contentSize, 然后設(shè)置一個(gè)透明的tableheaderview來(lái)代替設(shè)置tableView.contentInset快被自己的機(jī)智感動(dòng)了.
這樣既可以繞過(guò)上面說(shuō)的問(wèn)題了. 說(shuō)干就干.
需要在cell的model文件里面增加一個(gè)記錄本cell高度的屬性:
// 計(jì)算出來(lái)的cell的高度
@property (nonatomic, assign) CGFloat cellHeight;
同時(shí), 也得在控制器文件里面增加一個(gè)記錄所有cell總高度的屬性:
// cell的疊加高度
@property (nonatomic, assign) CGFloat cellsTotalHeight;
并且寫(xiě)了它的懶加載方法, 這樣每次在獲取總高度的時(shí)候都能保證是最新的
- (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;
}
同時(shí), 返回頭部視圖和高度是這樣de(為了方便調(diào)試, 先把頭部視圖設(shè)置成有個(gè)半透明色)
#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;
}
跑出來(lái)是這樣的:
倒過(guò)來(lái)加header.gif
這時(shí)候再把tableheaderview的背景色改成透明的, 就成了這樣了:
倒過(guò)來(lái)加header透明.gif
果然完美解決了, 這時(shí)候其實(shí)只剩一天時(shí)間假裝沒(méi)完(偷)成(lan)了.
不過(guò)還是要注意一下, 看圖倒過(guò)來(lái)加header.gif將tableheaderview改成透明之后, 新增cell還是會(huì)被tableheaderview擋住, 所以最后一個(gè)cell是點(diǎn)擊不到的. 因?yàn)槲翼?xiàng)目里面的cell不需要點(diǎn)擊, 所以這個(gè)問(wèn)題我可以忽略; 如果小伙伴項(xiàng)目里面的cell需要點(diǎn)擊的話, 就要將tableheaderview高度進(jìn)一步減去新增cell的高度哦. 這里我就不做具體代碼了, 小伙伴們自己探索一下把.
三. 小總結(jié)
總結(jié)起來(lái), 這里面幾個(gè)關(guān)鍵點(diǎn)是:
1.旋轉(zhuǎn)180°
2.不使用reloaddata方法, 而是使用inser....和scroll...兩個(gè)方法
3.通過(guò)增加tableheaderview來(lái)實(shí)現(xiàn)倒轉(zhuǎn)tableview之后cell的頂置, 并且動(dòng)態(tài)的改變tableheaderview的高度.
這個(gè)雖然只是項(xiàng)目的一小部分, 但每次解決問(wèn)題之后都能獲得成就感, 這就是碼農(nóng)單純的快樂(lè)吧.