前言其实我早就打算写这篇文章了,只是一直没有系统整理相关的demo。再加上我最近离职了,各种事情让我有点郁闷。所以一直在拖延。回家休息了一会儿,才想起自己写了一半的demo。我在找工作之前抽空完善了一下,然后写了一个说明文档作为备忘。需求背景自适应单元格行高在iOS中是一个很常见的需求,也是一个很简单的需求。之前遇到过很多不知道怎么实现的朋友。下面我一步一步来分析,供大家参考。问题分析其他实现场景我就不说了。现在具体需求分析一下,如图:Cellrowheightadaptation.png其实主要实现这几点就可以解决所谓的自适应行高问题。让我们一步步实现这个需求。计算UITableViewCell的高度说到计算高度,大家都很熟悉了。最简单常用的方法就是计算每个subview的高度并累加返回我们需要的cell高度,然后在UITableViewDelegate中调用:-(CGFloat)tableView:(UITableView*)tableViewheightForRowAtIndexPath:(NSIndexPath*)indexPath{return666;}或者固定高度时直接self.tableView.rowHeight=666;但是这就需要我们提前拿到model中的数据去手动计算每个控件的高度,这样比较麻烦而且不通用,所以autolayout出来后我们只需要在top,bottom,left,和右边的cell的contentView,系统会自动帮我们实现高度适配,也就是我们要保证cell的高度能被subview撑开就可以了,使用systemLayoutSizeFittingSizeAPI;iOS8以后就更简单了,直接用:self.tableView.estimatedRowHeight=666;self.tableView.rowHeight=UITableViewAutomaticDimension;就这样,其中estimatedRowHeight为预估高度,这里需要注意的是delegate中的returnheight方法不用写。关于这篇文章,UITableView+FDTemplateLayoutCel的作者写的一篇文章讲的很详细。建议先了解一下(优化UITableViewCell高度计算的那些事),但是这个方法实际上是在一个有多个子view的cell上滑动的。它非常卡Stuck,尤其是在iOS8上,尤其是在iOS10上。这与系统的高度计算机制有关。具体可以参考上面的文章,这里就不多说了。如果脱离autolayout,如果平时计算高度,最初是根据cell中子控件内容的高度手动累加的,但是这种方法每次都要手动处理高度计算逻辑,切换时横竖屏需要重新计算,正常开发会浪费很多不必要的精力。于是接着在项目中调用layoutSubviews得到了子控件的实际frame,这样我们就可以得到我们需要的cell高度值,如下代码所示:cell.frame=CGRectSetWidth(cell.frame,contentViewWidth);cell.contentView.frame=CGRectSetWidth(cell.contentView.frame,CGRectGetWidth(tableView.frame));[celllayoutIfNeeded];UIView*cellBottomView=nil;if(cell.FS_cellBottomView){cellBottomView=cell.FS_cellBottomView;}elseif(cell...}}else{NSArray*contentViewSubViews=cell.contentView.subviews;if(contentViewSubViews.count==0){cellBottomView=cell.contentView;}else{cellBottomView=contentViewSubViews[0];for(UIView*viewincontentViewSubViews){if(CGRectGetMaxY(视图.frame)>CGRectGetMaxY(cellBottomView.frame)){cellBottomView=view;}}}}CGFloatcellHeight=CGRectGetMaxY(cellBottomView.frame)+bottomOffset;哪里细胞博ttomView是位于单元格底部的子视图。为了提高计算效率,必须传入。如果不确定到底是哪个子视图,可以传入一个视图数组contentViewSubViews。详细使用请参考demo缓存cell的高度计算出来后,正常情况下已经满足了我们的要求,但是如果由于cell的复用机制,每次滑动都要重新计算高度值,如果cell的自定义样式很复杂,child如果视图太多,大量的计算肯定会消耗性能,造成明显的卡顿,所以缓存机制是必要的措施,何况苹果也推荐这样做;demo提供了两个计算行高的API:/**cell自动计算行高@paramtableViewtableView@paramindexPathindexPath@paramcontentViewWidthcell内容宽度,如果不确定可以通过0@returncellheight*/+(CGFloat)FSCellHeightForTableView:(UITableView*)tableViewindexPath:(NSIndexPath*)indexPathcellContentViewWidth:(CGFloat)contentViewWidthbottomOffset):(CGFbottomOffset;/**cell自动计算行高优化版@paramtableViewtableView@paramindexPathindexPath@paramcacheKey当前cell唯一标识@paramcontentViewWidthcell内容宽度,如果你是不确定,你可以通过0@returncellheight*/+(CGFloat)FSCellHeightForTableView:(UITableView*)tableViewindexPath:(NSIndexPath*)indexPathcacheKey:(NSString*)cacheKeycellContentViewWidth:(CGFloat)contentViewWidthbottomOffset:(CGFloat)bottomOffset;第一个使用用于缓存的数组,传入对应cell的indexPath作为数组索引va卢;第二种是使用字典来缓存数据,需要传入唯一标识cacheKey来区分;两种方法都可以准确获取cell高度,第一种实现方式更简洁。缺点是当数据源改变时,所有的缓存都会被清除并重新计算,比如reloadData时;二是在前者的基础上加个标识来区分不同的cell字符,使用时建议使用第二种方式,不会清除缓存数据,和轻量级页面没有区别。:withRowAnimation:),@selector(deleteSections:withRowAnimation:),@selector(reloadSections:withRowAnimation:),@selector(moveSection:toSection:),@selector(insertRowsAtIndexPaths:withRowAnimation:),@selector(deleteRowsAtIndexPathAnimation:withRow)@selector(reloadRowsAtIndexPaths:withRowAnimation:),@selector(moveRowAtIndexPath:toIndexPath:)兼容横竖屏。这个需求实现起来比较简单,就是横竖屏分别使用两组缓存数据,互不影响。横竖屏数据源切换时自动切换。-(NSMutableArray*)indexCacheArrForCurrentOrientation{returnUIDeviceOrientationIsPortrait([UIDevicecurrentDevice].orientation)?self.indexCacheArr_Portrait:self.indexCacheArr_Landscape;}***效果如图:FSAutoAdjust-cellHeightDemo.jpg
