你的位置:首页 > 软件开发 > 操作系统 > UITableViewCell 高度计算从混沌初始到天地交泰

UITableViewCell 高度计算从混沌初始到天地交泰

发布时间:2015-10-14 12:00:11
[原创]UITableViewCell 高度计算从混沌初始到天地交泰 本文主要基予iOS UITableViewCell 高度自适应计算问题展开陈述,废话少说直入正题: UITableView控件可能是iOS中大家最常用的控件了(滚动视图、cell重用、卡顿优化),今天要 ...

UITableViewCell 高度计算从混沌初始到天地交泰

[原创]UITableViewCell 高度计算从混沌初始到天地交泰

 

本文主要基予iOS UITableViewCell 高度自适应计算问题展开陈述,废话少说直入正题:

 

UITableView控件可能是iOS中大家最常用的控件了(滚动视图、cell重用、卡顿优化),今天要讨论的不是这些高大上的话题,今天的话题只是cell高度的计算。

* 传统frame布局下UITableViewCell 高度计算

* AutoLayout下UITableViewCell高度计算(iOS6、7)

* UITableViewCell高度计算之iOS8抽风之旅

* UITableViewCell高度计算之大一统

*第三方库UITableView-FDTemplateLayoutCell源码抛析

以下demo都是在cell高度可变的基础上进行的

一、传统frame布局下UITableViewCell 高度计算

        1、史上最传统的UITableViewCell使用方法(号称又笨又老),相信大家都用过这种,纯frame布局,cell定制,手动传入数据通过手动计算每一行cell的高度,代码都不好意思上了。

还是上下之前的demo吧!

01-UITableViewCell-frame

主要是在UITableViewCell(subCell)中使用一个静态方法传入数据并手动计算内容的高度

说到手动计算内容的高度,其实在cell里面大多是计算一些UILabel具体的宽高,根据内容计算UILabel对应的宽高,看下具体的API:

@interface NSString(UIStringDrawing)// Single line, no wrapping. Truncation based on the NSLineBreakMode.- (CGSize)sizeWithFont:(UIFont*)fontNS_DEPRECATED_IOS(2_0,7_0,"Use -sizeWithAttributes:");- (CGSize)sizeWithFont:(UIFont*)font forWidth:(CGFloat)width lineBreakMode:(NSLineBreakMode)lineBreakModeNS_DEPRECATED_IOS(2_0,7_0,"Use -boundingRectWithSize:options:attributes:context:");// Wrapping to fit horizontal and vertical size. Text will be wrapped and truncated using the NSLineBreakMode. If the height is less than a line of text, it may return// a vertical size that is bigger than the one passed in.// If you size your text using the constrainedToSize: methods below, you should draw the text using the drawInRect: methods using the same line break mode for consistency- (CGSize)sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)sizeNS_DEPRECATED_IOS(2_0,7_0,"Use -boundingRectWithSize:options:attributes:context:");// Uses NSLineBreakModeWordWrap- (CGSize)sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)size lineBreakMode:(NSLineBreakMode)lineBreakModeNS_DEPRECATED_IOS(2_0,7_0,"Use -boundingRectWithSize:options:attributes:context:");// NSTextAlignment is not needed to determine size
 

(2)、部分实现处理代码

ViewController中部分代理方法

- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView{return1;}- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section{    return [self.dataArraycount];}- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{static NSString *cellIdentifier = @"HomeCell";HomeCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];cell.content.text= [self.dataArray objectAtIndex:indexPath.row];CGFloat preMaxWaith =[UIScreen mainScreen].bounds.size.width-108;[cell.contentset PreferredMaxLayoutWidth:preMaxWaith];[cell.contentlayout IfNeeded];returncell;}-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{staticHomeCell*cell =nil;static dispatch_once_ tonceToken;//只会走一次dispatch_once(&onceToken, ^{cell = (HomeCell*)[tableView dequeueReusableCellWithIdentifier:@"HomeCell"];});//calculateCGFloatheight = [cell calulateHeightWithtTitle:[self.dataArray objectAtIndex:indexPath.row]desrip:[self.dataArray objectAtIndex:indexPath.row]];returnheight;}HomeCell.m -(CGFloat)calulateHeightWithtTitle:(NSString*)title desrip:(NSString*)descrip{//这里非常重要CGFloat preMaxWaith =[UIScreen mainScreen].bounds.size.width-108;[self.contentset PreferredMaxLayoutWidth:preMaxWaith];//[self.titleLabel setText:title];//这也很重要[self.content layoutIfNeeded];[self.content setText:descrip];[self.contentView layoutIfNeeded];CGSizesize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];//加1是关键returnsize.height+1.0f;}
heightForRowAtIndexPath:cell计算对比

由于iOS7之后,tableview 提供了estimatedHeightForRowAtIndexPathCount的API,这就对cell高度计算的方法调用次数产生了影响。

下面首先说下estimatedHeightForRowAtIndexPathCount :

// Use the estimatedHeight methods to quickly calcuate guessed values which will allow for fast load times of the table.// If these methods are implemented, the above -tableView:heightForXXX calls will be deferred until views are ready to be displayed, so more expensive logic can be placed there.- (CGFloat)tableView:(UITableView*)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath*)indexPathNS_AVAILABLE_IOS(7_0);

1、FDTemplateLayoutCell之所以能够做到兼容到所有的系统版本下的tableview,主要在于它维护了一套自己的cell高度缓存,同时有效的利用了tableview的高度预估的功能。从新定义新的cell高度缓存策略,这一点解决了只有iOS7下系统才会主动缓存cell高度的这一难点,有了FDiOS8、9下也能使用缓存高度

2、开启UITableView高度预估功能,优化heightForRowAtIndexPath的调用累计次数

(tableView:estimatedHeightForRowAtIndexPath: NS_AVAILABLE_IOS(7_0);)

由上可以看出estimatedHeightForRowAtIndexPath是iOS7才有的,iOS6是没有这个代理的,这个时间不仅要问,难道要iOS必须支持iOS7+以上才能使用,答案当然不是,系统的API早已做了优化,estimatedHeightForRowAtIndexPath在iOS6下面默认是可以被忽略的,不会因为版本的问题引起异常。在iOS6下高度计算的策略会跟iOS8、9下有点类似,使用FD自己提供的缓存,也能达到有效的减少计算cell高度带来的开销。

五、FDTemplateLayoutCell源码抛析

     谈到FD,首先熟悉下之前的一个知识点, iOS知识点整理-RunLoop。可能有些老生常谈了,也有可能部分童鞋看到直接晕掉了,其实大多iOS里面大多第三方库的手段无外乎就是runtime(这个东西在java中叫reflact,java里面有AOP , iOS 其实跟这个差不多)、CF这些黑魔法之类的东西来进行偷天换日、移花接木。

小结:iOS 中的runloop

        1、NSRunLoop提供了面向对象的API,但这些API不是线程安全的

        2、CFRunLoopRef提供了纯C函数的API,所有这些API都是线程安全的

  NSRunLoop是cocoa提供的,这个东西可能大多人还是经常使用的,cell里面更新异步下载成功的图片、启用一个timer追加到当前的应用循环中、启用一个常驻线程等;

  CFRunLoopRef可能就相对陌生些,CF开头跟定就是CoreFoundation中定义的,可以暂时理解为每个线程都有一个对应的runloop, 在一个runloop中可以有多种Model(模式),每个Mode又包含若干个source/Timer/Observe .

程序执行的时间当前runloop 只能存在一种Model,如果发生场景切换需要退出当前Model,进入下一个Model

系统一共提供了五种model:

 NSDefaultRunLoopMode:  App默认Mode,当没有接收到ScrollView滚动是,主线程通常使用这个Mode NSTrackingRunLoopMode: 到接收到ScroolView或其子类的时候,主线程就会切换到这个模式下运行。 UIInitializationRunLoopMode:当App启动时使用的第一个Mode,当启动完成后不再使用。 NSRunLoopCommonModes,是一个tag,本质上不是一个Mode,默认          

FD组件已经作了很好的封装,在heightForRowAtIndexPath中调用fd计算高度的方法,

- (CGFloat)fd_heightForCellWithIdentifier:(NSString*)identifier cacheByIndexPath:(NSIndexPath*)indexPath configuration:(void(^)(id))configuration{if(!identifier || !indexPath) {return0;}// Enable auto cache invalidation if you use this "cacheByIndexPath" API.if(!self.fd_autoCacheInvalidationEnabled) {self.fd_autoCacheInvalidationEnabled=YES;}// Enable precache if you use this "cacheByIndexPath" API.if(!self.fd_precacheEnabled) {self.fd_precacheEnabled=YES;// Manually trigger precache only for the first time.[selffd_precacheIfNeeded];}// Hit the cacheif([self.fd_cellHeightCachehasCachedHeightAtIndexPath:indexPath]) {[selffd_debugLog:[NSStringstringWithFormat:@"hit cache - [%@:%@] %@",@(indexPath.section),@(indexPath.row),@([self.fd_cellHeightCachecachedHeightAtIndexPath:indexPath])]];return[self.fd_cellHeightCachecachedHeightAtIndexPath:indexPath];}// Call basic height calculation method.CGFloatheight = [selffd_heightForCellWithIdentifier:identifierconfiguration:configuration];[selffd_debugLog:[NSStringstringWithFormat:@"cached - [%@:%@] %@",@(indexPath.section),@(indexPath.row),@(height)]];// Cache it[self.fd_cellHeightCachecacheHeight:heightbyIndexPath:indexPath];returnheight;}

原标题:UITableViewCell 高度计算从混沌初始到天地交泰

关键词:ie

ie
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。