服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - IOS - 关于iOS自适应cell行高的那些事儿

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

2021-05-17 16:29daisy IOS

这篇文章主要给大家介绍了关于iOS自适应cell行高的那些事儿,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一看看吧

前言

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

需求背景

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

问题分析

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

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

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

计算uitableviewcell的高度

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

?
1
2
3
4
- (cgfloat)tableview:(uitableview *)tableview heightforrowatindexpath:(nsindexpath *)indexpath
{
return 666;
}

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

?
1
self.tableview.rowheight = 666;

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

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

?
1
2
self.tableview.estimatedrowheight = 666;
self.tableview.rowheight = uitableviewautomaticdimension;

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
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的标识符,使用时还是建议使用第二种,不会清空缓存数据,轻量级页面没什么区别。总之两种方法都做了缓存数据的容错处理,支持以下方法:

?
1
2
3
4
5
6
7
8
9
@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:)

兼容横竖屏

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

?
1
2
3
4
- (nsmutablearray *)indexcachearrforcurrentorientation
{
return uideviceorientationisportrait([uidevice currentdevice].orientation) ? self.indexcachearr_portrait: self.indexcachearr_landscape;
}

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

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

总之答题思路就是这些,使用方便,感兴趣的可以移步下载demo查看:fsautoadjust-cellheightdemo

总结

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

原文链接:https://www.jianshu.com/p/16a67c58c8c4

延伸 · 阅读

精彩推荐
  • IOSIOS开发之字典转字符串的实例详解

    IOS开发之字典转字符串的实例详解

    这篇文章主要介绍了IOS开发之字典转字符串的实例详解的相关资料,希望通过本文能帮助到大家,让大家掌握这样的方法,需要的朋友可以参考下...

    苦练内功5832021-04-01
  • IOS关于iOS自适应cell行高的那些事儿

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

    这篇文章主要给大家介绍了关于iOS自适应cell行高的那些事儿,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的...

    daisy6092021-05-17
  • IOS解析iOS开发中的FirstResponder第一响应对象

    解析iOS开发中的FirstResponder第一响应对象

    这篇文章主要介绍了解析iOS开发中的FirstResponder第一响应对象,包括View的FirstResponder的释放问题,需要的朋友可以参考下...

    一片枫叶4662020-12-25
  • IOSiOS 雷达效果实例详解

    iOS 雷达效果实例详解

    这篇文章主要介绍了iOS 雷达效果实例详解的相关资料,需要的朋友可以参考下...

    SimpleWorld11022021-01-28
  • IOSiOS通过逆向理解Block的内存模型

    iOS通过逆向理解Block的内存模型

    自从对 iOS 的逆向初窥门径后,我也经常通过它来分析一些比较大的应用,参考一下这些应用中某些功能的实现。这个探索的过程乐趣多多,不仅能满足自...

    Swiftyper12832021-03-03
  • IOSiOS布局渲染之UIView方法的调用时机详解

    iOS布局渲染之UIView方法的调用时机详解

    在你刚开始开发 iOS 应用时,最难避免或者是调试的就是和布局相关的问题,下面这篇文章主要给大家介绍了关于iOS布局渲染之UIView方法调用时机的相关资料...

    windtersharp7642021-05-04
  • IOSiOS中tableview 两级cell的展开与收回的示例代码

    iOS中tableview 两级cell的展开与收回的示例代码

    本篇文章主要介绍了iOS中tableview 两级cell的展开与收回的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    J_Kang3862021-04-22
  • IOSIOS 屏幕适配方案实现缩放window的示例代码

    IOS 屏幕适配方案实现缩放window的示例代码

    这篇文章主要介绍了IOS 屏幕适配方案实现缩放window的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要...

    xiari5772021-06-01