亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

對tableView三種計算動態行高方法的分析

2019-11-14 18:48:12
字體:
來源:轉載
供稿:網友

tableView是一個神奇的東西,可以這么說,就算是一個初學者如果能把tableView玩的很6,那編一般的iOS的需求都問題不大了。tableView是日常開發中用爛了的控件,但是關于tableView中的自定義cell的動態行高,還是有一些玄機的。筆者本次主要是因為預估行高的方法的問題作為了一個契機順帶寫了此文對幾種動態行高方法的分析。

如果你不是在董鉑然博客園看到本文,請點擊查看原文。

 

舊方法

現在常規的動態行高的計算方法還是用

[str boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size

這其中需要先傳入一個最大尺寸和一個屬性字典,特殊的格式要求都寫在屬性字典中。

 NSDictionary *attrs = @{NSFontAttributeName : font};

整個流程的基本思想大概就是:用一個字符串對象來調用此方法,中間需要傳入一個屬性字典來告知字體和樣式,然后根據字符串長度的多少來算出應該給多大的frame。前面傳進的size一般可以設置最大寬度。 此方法一般寫成分類便于調用。

#import "NSString+Size.h"@implementation NSString (Size)/** *  類方法計算size大小 */+ (CGSize)sizeWithString:(NSString *)str andFount:(UIFont *)font andMaxSize:(CGSize)size{    NSDictionary *attrs = @{NSFontAttributeName : font};    return [str boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;}/** *  對象方法計算size大小 */- (CGSize)sizeWithFount:(UIFont *)font andMaxSize:(CGSize)size;{    NSDictionary *attrs = @{NSFontAttributeName : font};    return [self boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;}@end

這些方法從字面上看也比較容易理解。

調用時的代碼基本就是取到一個字符串,傳入一個font和一個最大size,如下把寬設置成了270就是最大寬度為270高度往下順延的話就把高度寫成MAXFLOAT

    NSString *text = _message.text;    CGSize textSize = [text sizeWithFount:[UIFont systemFontOfSize:14] andMaxSize:CGSizeMake(270, MAXFLOAT)];

然后在frame中取到最下面一個空間的maxY,從而讓每一個cell在set方法中就得到自己的行高 ,然后通過cell的類方法返回。

 

新方法

隨著iOS8的自動布局和Interface builder越來越成熟,逐漸衍生出了一種先用storyboard或xib界面再算自定義行高的方法。

這種方法一般需要先搭建一個圖形化界面。如下圖大概搭一個比較復雜的cell。

圖1

首先可以清晰的看出,用IB搭建看上去很快就能搭建完畢,并且有的圖片或是view的背景設置了之后能看出界面大概的感覺。這里需要注意的就是label設置約束的方法,普通控件一般都要設置四個約束才能固定位置,label和button只設置兩個約束(只需要寫固定位置的兩條約束,不需要寫自身寬高的約束)也不會報錯,但是需要在editor中設置sizeToFit,這樣可以根據字數自動給你分配一個控件的大小。

圖2

一般評論類的label肯定都是字數比較多的,這時2條約束就不夠了需要再設置一個最大寬度的約束,如圖1我設置的方法是,把評論label與左右邊界的間隙給設定了,這個在IB中叫Leading(前)和Training(后),高的約束我們沒有寫如果字數超過了一行他就會自己往下順延。 這么寫相較于把寬度約束寫死的好處是會自動根據屏幕適配不管屏幕多大都是左右空出若干像素。這樣做也有局限性,就是假設給這個label設置一個背景色,如果字數就5個字背景色也會延伸到一整行。如果QQ聊天頁面也這樣做,不管是幾個字都是一整行的聊天氣泡那會很丑。于是有了一種少則背景也少,多也不超過最大寬度的做法,就是設置label的width的less than來設置最大寬度。這么做如果字數不足一行的話,約束也會自動縮到與label長度匹配。

如果這個頁面用純手碼寫,可想而知會非常麻煩。

用IB頁面做自定義行高的計算方法也更簡單。也就是里面模型的set方法正常寫,給自己的UI控件賦值。然后在tableView的行高方法heightForRow中,先給cell的模型賦值,然后再使用一次

[cell layoutIfNeeded];

他會自動根據填進去數值來布局,然后我們直接在這個方法中返回最下面一個控件的bottom位置+若干間隙,以此來作為行高即可。

真正的布局其實也就是用了這一行代碼,并且可以做到屏幕適配不用if判斷各種frame。但這樣寫也有一些問題,首先就是這么寫從結構上來看不合理。這個行高方法中不應該寫這些賦值語句。官方還是其他大神說不合理的原因,應該是這個方法應該僅僅是用來算出行高并顯示的,會調用多次,如果在這里賦值性能會很差。這么說有道理,把這里面的每行代碼都看一遍,能看出性能較差的方法主要就是這兩行:1.給cell里模型賦值  2.layoutIfNeed 。如果調用多次這個方法那這兩行也會執行多次,所以這應該是不科學的。  我實際的做法是在其中設置一個行高緩存字典,并且找一個肯定不會重復的標識來做key值。每一行cell計算行高前都先拿自己的id去行高緩存字典里取一下看有沒有值,如果有則直接返回對應的value,如果沒有再計算。這樣可以使這性能比較差得兩行代碼只執行一次。達到優化效果。

            MTFBNoReplyCell *feedbackNoreplyCell = [MTFBNoReplyCell cell];                        NSString *thisId =[NSString stringWithFormat:@"%d", feedbackModel.feedbackid];            //            MTLog(@"%@",[self.cellHeightCache valueForKey:thisId]);            CGFloat cacheHeight = [[self.cellHeightCache valueForKey:thisId] doubleValue];            if (cacheHeight) {//            MTLog(@"返回緩存的行高");                return cacheHeight;            }//            MTLog(@"耗性能的行高");            feedbackNoreplyCell.feedbackDetailModel = feedbackModel;            [feedbackNoreplyCell layoutIfNeeded];                        [self.cellHeightCache setValue:@(feedbackNoreplyCell.replyBtn.bottom+16) forKey:thisId];                        return feedbackNoreplyCell.replyBtn.bottom+16;

大概的思想如上所示。 如果這個tableView的數據不會隨時改變較為固定的話,可以把取到的模型作為value以indexpath.row為key存一個緩存字典這樣也能優化一些。行高方法里取過了,cellForRow就可以直接用了。(董鉑然)

 

預估行高方法

這里我想重點說一下這個預估行高的方法estimatedHeightForRowAtIndexPath 。這個方法可能大部分人一說到這個,就說這個方法好啊,預估行高方法可以減少heightForRow的調用次數,使得性能達到優化。 孰不知實際運用中是存在著一定問題的。

就拿整個tableView來說 他是繼承自scrowView的,scrowView能夠滾動是因為它有contentSize。tableView在初次加載的時候也需要算出自己的contentSize(而且會算不止一次),也就是說需要調一下所有的行高方法然后自己內部給他累加一下算出整個contentSize。如果在行高方法里設置一個打印會看到方法會調用很多次。這時如果有一個預估方法return 100。那它就能很快算出總值了。就會減少行高方法的調用,在實際用到某一行時再調用。

但是可能會出現如下左圖的問題。

     

問題的原因就是,一開始預估方法給每行預估了一個行高,然后后面實際加載的行高與預估的行高不合時,會出現cell上下的“竄動”給人卡卡的感覺。對此我的思想是,如果是動態的且cell的復雜度較高,行與行之間差距大的時候,就直接不要寫預估行高方法了吧,讓他自己算吧哪怕多調用幾次,畢竟上面已經寫過緩存行高字典了,性能姑且是可以hold住了,并且不會出現“竄動”情況。如右圖所示。

但是如果是固定行高有一種或是三種不同的cell,行高分別是120,150,200。你在預估行高了寫個return 150。遇到行高與預估不等時,卻也不會出現“竄動”。我推測應該是estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同時存在,這兩者同時存在才會出現“竄動”的bug。所以我的建議是:只要是固定行高就寫預估行高來減少行高調用次數提升性能。如果是動態行高就不要寫預估方法了,用一個行高的緩存字典來減少代碼的調用次數即可。

關于上面行高的新方法和舊方法的對比,我的總結是:首先新方法肯定性能上是比舊方法要差一些的。具體體現在兩個方面,1是在IB頁面開發的東西,程序一啟動就會全部加載進內存由系統托管,以至于有的界面你已經把導航控制器的棧頂控制器給pop了,發現內存還沒有下降。2是新方法和舊方法有一個本質的區別,舊方法是直接算,算你需要多大的尺寸就告訴你,新方法則是先強制布局然后看你占了多大的尺寸再告訴你,這兩者一對比,新方法就是多了一個強制布局的過程,這肯定是會對性能造成一定影響的,那具體影響多少?關于滑動計算行高我還不知道有什么可以明確一個數據的對比,我只能用肉眼看屏幕的滑動來區分對比,我的感覺就是基本沒差別,如果要說有的話新方法可能會非常輕微的卡頓,換而言之就是同一個頁面,舊方法編完需要10小時,新方法編完需要3小時,但是新方法的性能略差于舊方法。就看你自己怎么衡量了。當然非常龐大的項目還是建議用舊方法,畢竟一點一點的“略差于”積累在一起就是很差了。

 

關于iOS8新的行高特性

首先是有了一個新的用法。寫在viewdidload里

self.tableView.estimatedRowHeight = 50.0f;self.tableView.rowHeight = UITableViewAutomaticDimension;

這就沒什么好說的了,蘋果自己幫你把動態行高計算了,所有亂七八糟的都不用管了。 但是暫時說這些基本沒用,因為現在還看不到哪個公司的項目不適配iOS7,就算出了iOS9感覺也不會讓你直接適配iOS8的,iOS7還會存在相當長一段時間,畢竟以后新系統版本改變應該都不會有iOS6到7變化那么大了,除非啥時候蘋果總設計師喬納森伊夫下臺了。

以上也都是我個人對行高方法的理解,如有哪里理解的不對或是太片面歡迎指出。 

如果你不是在董鉑然博客園看到本文,請點擊查看原文


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品美女午夜av| 最近2019中文字幕mv免费看| 国产亚洲精品成人av久久ww| 久久久久久噜噜噜久久久精品| 国产一区二区三区高清在线观看| 高清一区二区三区日本久| 国产一区视频在线| 久久久免费高清电视剧观看| 日本久久久久亚洲中字幕| 日韩亚洲在线观看| 亚洲男人天天操| 色悠悠国产精品| 久久久欧美精品| 日韩在线中文字| 日韩电影免费在线观看中文字幕| 一区二区三区视频免费在线观看| 欧洲亚洲女同hd| 91精品久久久久久久久久久| 精品国内产的精品视频在线观看| 国产精品久久久久久久久久三级| 久久久99久久精品女同性| 亚洲va码欧洲m码| 精品国内自产拍在线观看| 欧美肥臀大乳一区二区免费视频| 亚洲欧美国内爽妇网| 亚洲第一视频在线观看| 亚洲精品动漫100p| 国产精品视频99| 亚洲男人第一av网站| 亚洲色图偷窥自拍| 亚洲人成网站777色婷婷| 欧美激情a∨在线视频播放| 久久久av一区| 精品久久久久久久久国产字幕| 青青草原一区二区| 亚洲最大福利视频网| 黑丝美女久久久| 国产在线精品播放| 日韩视频亚洲视频| 久久久噜噜噜久噜久久| 欧美日韩加勒比精品一区| 国产精品久久久久久久久久东京| 91亚洲午夜在线| 成人精品视频久久久久| 色综合天天狠天天透天天伊人| 亚洲国产精品成人av| 国产精品三级久久久久久电影| 97国产在线视频| 欧美性受xxx| 久久精品国产69国产精品亚洲| 国产一区二区三区视频在线观看| 18性欧美xxxⅹ性满足| 久久免费视频网| 亚洲欧洲午夜一线一品| www国产亚洲精品久久网站| 亚州精品天堂中文字幕| 日韩精品免费综合视频在线播放| 日韩中文字幕在线视频播放| 成人免费在线视频网站| 久久久久久久久久久成人| 不卡伊人av在线播放| 成人a在线视频| 人人做人人澡人人爽欧美| 91精品久久久久久久久久久| 欧美自拍视频在线| 亚洲国产精品yw在线观看| 欧美性理论片在线观看片免费| 国产在线精品一区免费香蕉| 日韩av电影国产| 国产亚洲精品久久久久久牛牛| 久久久av亚洲男天堂| 国产日产欧美a一级在线| 国产视频亚洲视频| 欧美日韩国产成人在线观看| 欧美视频不卡中文| 一个人看的www久久| 久久亚洲国产成人| 久久99精品久久久久久青青91| 国产日韩精品在线播放| 日韩在线观看成人| 亚洲欧美日韩视频一区| 亚洲国产精品久久| 亚洲国产天堂久久国产91| 26uuu另类亚洲欧美日本一| 亚洲欧洲一区二区三区在线观看| 日韩有码视频在线| 91精品国产色综合| 8090理伦午夜在线电影| 麻豆国产精品va在线观看不卡| 色多多国产成人永久免费网站| 久久免费高清视频| 久久综合伊人77777蜜臀| 26uuu亚洲国产精品| 在线观看国产欧美| 成人xvideos免费视频| 亚洲色图13p| 国产精品视频精品视频| 国产一区二区三区在线视频| 国产免费一区二区三区在线观看| 久久国产精品99国产精| 国产成人精品久久二区二区91| 91伊人影院在线播放| 国产精选久久久久久| 欧美一区二区三区精品电影| 一区二区三区视频免费在线观看| 亚洲人成电影在线观看天堂色| 亚洲欧美国产一本综合首页| 亚洲欧美国产精品专区久久| 久久精品中文字幕电影| 亚洲国产成人爱av在线播放| 日本一区二区三区四区视频| 欧美日韩成人精品| 九九热这里只有在线精品视| 国产日韩换脸av一区在线观看| 亚洲裸体xxxx| 色播久久人人爽人人爽人人片视av| 亚洲人成欧美中文字幕| 日本欧美中文字幕| 国产成人精品在线观看| 成人午夜小视频| 日韩精品视频在线观看网址| 色琪琪综合男人的天堂aⅴ视频| 日韩精品在线观| 日韩中文字幕在线视频播放| 欧美综合一区第一页| 日韩av在线免费观看| 久久成人国产精品| 国产精品69精品一区二区三区| 中文字幕亚洲国产| 日韩中文字幕亚洲| 午夜精品99久久免费| 亚洲激情小视频| 亚洲国产99精品国自产| 欧美大片网站在线观看| 中文字幕亚洲精品| 97成人精品区在线播放| 精品亚洲夜色av98在线观看| 91干在线观看| 亚洲人在线视频| 久久福利视频网| 成人久久18免费网站图片| 日韩欧美国产激情| 午夜精品一区二区三区在线播放| 欧美日在线观看| 国产精品久久电影观看| 亚洲人成电影在线观看天堂色| 97超碰国产精品女人人人爽| 日韩中文有码在线视频| 日韩精品在线电影| 欧美日韩国产成人高清视频| 亚洲精品999| 国产精品久久久久久久7电影| 久久成人人人人精品欧| 国产精品欧美日韩一区二区| 国产亚洲精品综合一区91| 亚洲国产精品女人久久久| 91色视频在线观看| 国产精品入口尤物| 国产精品毛片a∨一区二区三区|国| 亚洲专区在线视频| 久久乐国产精品| 欧美性xxxx极品hd欧美风情| 亚洲视频777| 欧美精品久久久久|