关于iOS自适应cell行高的那些事儿

前言

其实早就准备写这篇文章了,但是一直没有系统去整理一下相关的demo,加上最近离职了,各种事情忙的有点郁闷,所以一直拖沓了下来。回家休息了一段时间想起来写了一半的demo,在还没找工作的这段空挡时间抽空完善了一下再写篇说明文档备忘一下。

需求背景

iOS的cell行高自适应是个非常常见的需求,也是一个非常简单的需求,之前我遇到过很多小伙伴不知道怎么来实现,在这里就一步步的来分析一下,供大家参考。

问题分析

其他的实现场景就不说了,我们现在来分析一下具体的需求,如图所示:

关于iOS自适应cell行高的那些事儿

其实主要实现这几点就可以解决所谓的自适应行高的问题,下面我们就来逐步实现这个需求。

计算UITableViewCell的高度

说到计算高度,大家都不陌生,最简单常见的就是计算出每个子视图的高度累积起来返回我们所需要的cell高度,然后在UITableViewDelegate中调用:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 666;
}

或者高度固定的情况下直接

self.tableView.rowHeight = 666;

但是这就要求我们需要提前拿到model中的数据来手动计算每个控件的高度,这样既麻烦又不能通用,所以在autolayout出来之后我们只要给cell的contentView的上下左右都添加了约束,系统就可以自动的帮我们实现高度的自适应,就是一定要保证cell的高度可以被子视图撑开就可以了,利用的是systemLayoutSizeFittingSize这个API;

iOS8之后就更简单了,直接使用:

self.tableView.estimatedRowHeight = 666;
self.tableView.rowHeight = UITableViewAutomaticDimension;

就可以了,其中estimatedRowHeight是预估高度,这里要注意delegate中的返回高度方法就不用在写了。

关于这方面的文章,UITableView+FDTemplateLayoutCel的作者写的一篇文章十分详细,建议先去了解一下(优化UITableViewCell高度计算的那些事)

但是这个方法实际上在有多个子视图的cell上滑动是很卡顿的,特别是在iOS8尤其是iOS10上卡顿尤为明显,这跟系统的算高机制有一定关系,具体可以看上面的文章,这里不再解释了。

如果脱离开autolayout来说,平时计算高度的话,最开始都是根据cell内子控件内容的高度来手动累加起来,但是这个方法每次都要去手动处理其中的算高逻辑,而且横竖屏切换的时候还要重新计算,在平时开发中就会浪费大量不必要的精力。所以后来我在项目中是通过调用layoutSubviews来获取到子控件的实际frame,这样就可以得到我们所需的cell高度值,如下代码所示:

cell.frame = CGRectSetWidth(cell.frame,contentViewWidth);
cell.contentView.frame = CGRectSetWidth(cell.contentView.frame,CGRectGetWidth(tableView.frame));
[cell layoutIfNeeded];

UIView *cellBottomView = nil;
if (cell.FS_cellBottomView) {
cellBottomView = cell.FS_cellBottomView;
}else if (cell.FS_cellBottomViews && cell.FS_cellBottomViews.count > 0) {
cellBottomView = cell.FS_cellBottomViews[0];
for (UIView *view in cell.FS_cellBottomViews) {
if (CGRectGetMaxY(view.frame) > CGRectGetMaxY(cellBottomView.frame)) {
cellBottomView = view;
}
}
}else {
NSArray *contentViewSubViews = cell.contentView.subviews;
if (contentViewSubViews.count == 0) {
cellBottomView = cell.contentView;
}else{
cellBottomView = contentViewSubViews[0];
for (UIView *view in contentViewSubViews) {
if (CGRectGetMaxY(view.frame) > CGRectGetMaxY(cellBottomView.frame)) {
cellBottomView = view;
}
}
}
}

CGFloat cellHeight = CGRectGetMaxY(cellBottomView.frame) + bottomOffset;

其中的cellBottomView是位于cell最底部的子视图,为了提高计算效率最好传入,如果不确定哪个子视图在最下面,可以传入一个视图数组contentViewSubViews,详细使用方式可以查看demo。

缓存cell高度

高度计算出来后,正常来说我们的需求已经达到了,但是如果这个高度值每次滑动的时候由于cell的复用机制都会重新计算,若果这个cell的自定义样式很复杂,子视图太多,那么大量的计算一定会损耗性能而导致明显的卡顿,所以缓存机制就是个必要的措施,更何况苹果也建议这样做;
demo提供了两个计算行高的API:

/**
cell自动计算行高

@param tableView tableView
@param indexPath indexPath
@param contentViewWidth cell内容宽度,不确定可传0
@return cell高度
*/
+ (CGFloat)FSCellHeightForTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath cellContentViewWidth:(CGFloat)contentViewWidth bottomOffset:(CGFloat)bottomOffset;

/**
cell自动计算行高优化版

@param tableView tableView
@param indexPath indexPath
@param cacheKey 当前cell唯一标识符
@param contentViewWidth cell内容宽度,不确定可传0
@return cell高度
*/
+ (CGFloat)FSCellHeightForTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath cacheKey:(NSString *)cacheKey cellContentViewWidth:(CGFloat)contentViewWidth bottomOffset:(CGFloat)bottomOffset;

第一种使用数组来做缓存,传入对应cell的indexPath作为数组索引值;第二种则采用字典来缓存数据,要求传入一个唯一标识符cacheKey来区分;

两种方式都可以准确获得cell高度,第一种实现更简洁,缺点就是数据源发生变化时,所有的缓存就会清空重新计算后缓存,比如reloadData的时候;第二种就是在前者的基础上添加一个区分不同cell的标识符,使用时还是建议使用第二种,不会清空缓存数据,轻量级页面没什么区别。总之两种方法都做了缓存数据的容错处理,支持以下方法:

@selector(reloadData),@selector(insertSections:withRowAnimation:),@selector(deleteSections:withRowAnimation:),@selector(reloadSections:withRowAnimation:),@selector(moveSection:toSection:),@selector(insertRowsAtIndexPaths:withRowAnimation:),@selector(deleteRowsAtIndexPaths:withRowAnimation:),@selector(reloadRowsAtIndexPaths:withRowAnimation:),@selector(moveRowAtIndexPath:toIndexPath:)

兼容横竖屏

这个需求实现较为简单,就是横屏和竖屏分别采用两套缓存数据,互不影响,切换横竖屏的时候自动切换数据源。

- (NSMutableArray *)indexCacheArrForCurrentOrientation
{
return UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation) ? self.indexCacheArr_Portrait: self.indexCacheArr_Landscape;
}

最后实现的效果如图所示:

关于iOS自适应cell行高的那些事儿

总之答题思路就是这些,使用方便,感兴趣的可以移步下载demo查看:FSAutoAdjust-cellHeightDemo (本地下载)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


当我们远离最新的 iOS 16 更新版本时,我们听到了困扰 Apple 最新软件的错误和性能问题。
欧版/美版 特别说一下,美版选错了 可能会永久丧失4G,不过只有5%的概率会遇到选择运营商界面且部分必须连接到iTunes才可以激活
一般在接外包的时候, 通常第三方需要安装你的app进行测试(这时候你的app肯定是还没传到app store之前)。
前言为了让更多的人永远记住12月13日,各大厂都在这一天将应用变灰了。那么接下来我们看一下Flutter是如何实现的。Flutter中实现整个App变为灰色在Flutter中实现整个App变为灰色是非常简单的,只需要在最外层的控件上包裹ColorFiltered,用法如下:ColorFiltered(颜色过滤器)看名字就知道是增加颜色滤镜效果的,ColorFiltered( colorFilter:ColorFilter.mode(Colors.grey, BlendMode.
flutter升级/版本切换
(1)在C++11标准时,open函数的文件路径可以传char指针也可以传string指针,而在C++98标准,open函数的文件路径只能传char指针;(2)open函数的第二个参数是打开文件的模式,从函数定义可以看出,如果调用open函数时省略mode模式参数,则默认按照可读可写(ios_base:in | ios_base::out)的方式打开;(3)打开文件时的mode的模式是从内存的角度来定义的,比如:in表示可读,就是从文件读数据往内存读写;out表示可写,就是把内存数据写到文件中;
文章目录方法一:分别将图片和文字置灰UIImage转成灰度图UIColor转成灰度颜色方法二:给App整体添加灰色滤镜参考App页面置灰,本质是将彩色图像转换为灰度图像,本文提供两种方法实现,一种是App整体置灰,一种是单个页面置灰,可结合具体的业务场景使用。方法一:分别将图片和文字置灰一般情况下,App页面的颜色深度是24bit,也就是RGB各8bit;如果算上Alpha通道的话就是32bit,RGBA(或者ARGB)各8bit。灰度图像的颜色深度是8bit,这8bit表示的颜色不是彩色,而是256
领导让调研下黑(灰)白化实现方案,自己调研了两天,根据网上资料,做下记录只是学习过程中的记录,还是写作者牛逼
让学前端不再害怕英语单词(二),通过本文,可以对css,js和es6的单词进行了在逻辑上和联想上的记忆,让初学者更快的上手前端代码
用Python送你一颗跳动的爱心
在uni-app项目中实现人脸识别,既使用uni-app中的live-pusher开启摄像头,创建直播推流。通过快照截取和压缩图片,以base64格式发往后端。
商户APP调用微信提供的SDK调用微信支付模块,商户APP会跳转到微信中完成支付,支付完后跳回到商户APP内,最后展示支付结果。CSDN前端领域优质创作者,资深前端开发工程师,专注前端开发,在CSDN总结工作中遇到的问题或者问题解决方法以及对新技术的分享,欢迎咨询交流,共同学习。),验证通过打开选择支付方式弹窗页面,选择微信支付或者支付宝支付;4.可取消支付,放弃支付会返回会员页面,页面提示支付取消;2.判断支付方式,如果是1,则是微信支付方式。1.判断是否在微信内支付,需要在微信外支付。
Mac命令行修改ipa并重新签名打包
首先在 iOS 设备中打开开发者模式。位于:设置 - 隐私&安全 - 开发者模式(需重启)
一 现象导入MBProgressHUD显示信息时,出现如下异常现象Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_MBProgressHUD", referenced from: objc-class-ref in ViewController.old: symbol(s) not found for architecture x86_64clang: error: linker command failed wit
Profiles >> 加号添加 >> Distribution >> "App Store" >> 选择 2.1 创建的App ID >> 选择绑定 2.3 的发布证书(.cer)>> 输入描述文件名称 >> Generate 生成描述文件 >> Download。Certificates >> 加号添加 >> "App Store and Ad Hoc" >> “Choose File...” >> 选择上一步生成的证书请求文件 >> Continue >> Download。
今天有需求,要实现的功能大致如下:在安卓和ios端实现分享功能可以分享链接,图片,文字,视频,文件,等欢迎大佬多多来给萌新指正,欢迎大家来共同探讨。如果各位看官觉得文章有点点帮助,跪求各位给点个“一键三连”,谢啦~声明:本博文章若非特殊注明皆为原创原文链接。