不等高cell纯代码:

对应的GitHub 项目链接:https://github.com/liminting/CalculateCellDemo

不等高cell

- 先设置高度rowH = 250

- 复习frame设置等高cell

1.头像:宽高都是30,距离top.left都是10

CGFloat iconX = ;

CGFloat iconY = ;

CGFloat iconW = ;

CGFloat iconH = ;

self.icon_Ima.frame = CGRectMake(iconX, iconY, iconW, iconH);

2.昵称:left距离icon_Ima为10,顶部和icon_Ima对齐,宽高根据文字大小决定

CGFloat nameX = ;

CGFloat nameY = ;

CGFloat nameW = ;

CGFloat nameH = ;

self.name_Lab.frame = CGRectMake(nameX, nameY, nameW, nameH);

3.vip:宽高14,距离昵称10,垂直方向与昵称中线对齐

CGFloat vipX = ;

CGFloat vipY = ;

CGFloat vipW = ;

CGFloat vipH = ;

self.vip_Ima.frame = CGRectMake(vipX, vipY, vipW, vipH);

4.正文:左边与头像对齐,右边距离与左边距离相同,顶部距离头像间距10,高度根据文字多少决定

CGFloat textX = ;

CGFloat textY = ;

CGFloat textW = ;

CGFloat textH = ;

self.text_Lab.frame = CGRectMake(textX, textY, textW, textH);

5.配图:宽高100,左边与正文对齐,顶部距离正文距离为10

CGFloat pictureX = ;

CGFloat pictureY = ;

CGFloat pictureW = ;

CGFloat pictureH = ;

self.picture_Ima.frame = CGRectMake(pictureX, pictureY, pictureW, pictureH);

BetterCellH

优化cellH的计算

reason: 代理方法heightForRowAtIndexPath以及cell布局方法layoutSubviews存在大量重复性代码,必须抽取出来

解决办法: remove其中较"晚"调用方法中的计算,将heightForRowAtIndexPath中的计算通过模型传递给之后用到的地方:cell布局方法layoutSubviews

- 1.模型中新添加frames属性

@property (nonatomic, assign) CGRect icon_Ima_frame; /**< 头像frame */

@property (nonatomic, assign) CGRect name_Lab_frame; /**< 昵称frame */

@property (nonatomic, assign) CGRect vip_Ima_frame; /**< vipframe */

@property (nonatomic, assign) CGRect text_Lab_frame; /**< 正文frame */

@property (nonatomic, assign) CGRect picture_Ima_frame; /**< 配图frame */

- 2.heightForRowAtIndexPath代理方法中计算所有子控件frame

根据indexPath返回cell的高度

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

{

NSLog(@"%s, line = %d, row = %ld", __FUNCTION__, __LINE__, indexPath.row);

XMGStatus *status = self.statuses[indexPath.row];

在这里先计算好cell的高度,然后返回

CGFloat margin = 10;

1.头像:宽高都是30,距离top.left都是10

CGFloat iconX = margin;

CGFloat iconY = margin;

CGFloat iconW = 30;

CGFloat iconH = iconW;

CGRect icon_Ima_frame = CGRectMake(iconX, iconY, iconW, iconH);

status.icon_Ima_frame = icon_Ima_frame;

2.昵称:left距离icon_Ima为10,顶部和icon_Ima对齐,宽高根据文字大小决定

CGFloat nameX = CGRectGetMaxX(icon_Ima_frame) + margin;

CGFloat nameY = iconY;

根据字体大小计算单行文字的size

CGSize nameSize = [status.name sizeWithAttributes:@{NSFontAttributeName: NameFont}];

CGFloat nameW = nameSize.width;

CGFloat nameH = nameSize.height;

CGRect name_Lab_frame = CGRectMake(nameX, nameY, nameW, nameH);

status.name_Lab_frame = name_Lab_frame;

3.vip:宽高14,距离昵称10,垂直方向与昵称中线对齐

CGFloat vipX = CGRectGetMaxX(name_Lab_frame) + margin;

CGFloat vipW = 14;

CGFloat vipH = 14;

CGFloat vipY = CGRectGetMidY(name_Lab_frame) - vipH * 0.5;

CGRectGetMidY(self.name_Lab.frame) = vipY + vipH * 0.5

CGRect vip_Ima_frame = CGRectMake(vipX, vipY, vipW, vipH);

status.vip_Ima_frame = vip_Ima_frame;

4.正文:左边与头像对齐,右边距离与左边距离相同,顶部距离头像间距10,高度根据文字多少决定

CGFloat textX = iconX;

CGFloat textY = margin + CGRectGetMaxY(icon_Ima_frame);

CGFloat textW = self.tableView.bounds.size.width - 2 * textX;

CGSize maxSize = CGSizeMake(textW, CGFLOAT_MAX);

CGSize textSize = [status.text boundingRectWithSize:maxSize

options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:

TextFont} context:nil].size;

CGFloat textH = textSize.height;

CGRect text_Lab_frame = CGRectMake(textX, textY, textW, textH);

status.text_Lab_frame = text_Lab_frame;

5.配图:宽高100,左边与正文对齐,顶部距离正文距离为10

CGRect picture_Ima_frame = CGRectZero;

if (status.picture) {

CGFloat pictureW = 100;

CGFloat pictureH = 100;

CGFloat pictureX = textX;

CGFloat pictureY = margin + CGRectGetMaxY(text_Lab_frame);

picture_Ima_frame = CGRectMake(pictureX, pictureY, pictureW, pictureH);

}

status.picture_Ima_frame = picture_Ima_frame;

CGFloat CellH = (status.picture)? CGRectGetMaxY(picture_Ima_frame) + margin: CGRectGetMaxY(text_Lab_frame) + margin;

return CellH;

}

- 3.由模型传入cell,在 layoutSubviews 中完成布局

3.在layoutSubviews方法中布局

- (void)layoutSubviews

{

[super layoutSubviews];

self.icon_Ima.frame = self.status.icon_Ima_frame;

self.name_Lab.frame = self.status.name_Lab_frame;

self.vip_Ima.frame = self.status.vip_Ima_frame;

self.text_Lab.frame = self.status.text_Lab_frame;

self.picture_Ima.frame = self.status.picture_Ima_frame;

}

总结:

- 重复计算问题解决

- 代理方法调用频繁,实际问题仍然严峻

根据问题继续优化

- 思考每个cell对应一个模型

- 在模型中新增属性cellH@property (nonatomic, assign) CGFloat cellH; /**< cell的高度 */

- 重写get方法,懒加载中计算

懒加载

- (CGFloat)cellH

{

if (!_cellH) {

在这里先计算好cell的高度,然后返回呢?

NSLog(@"%s, line = %d", __FUNCTION__, __LINE__);

...

_cellH = (self.picture)? CGRectGetMaxY(picture_Ima_frame) + margin: CGRectGetMaxY(text_Lab_frame) + margin;

}

return _cellH;

}

- 此时控制器中代理方法知道的很少

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

{

XMGStatus *status = self.statuses[indexPath.row];

return status.cellH;

}

additional(了解)

- cell中的布局可以在模型的set方法中写

- 新建XMGStatusFrame类

* Frame相关属性在声明文件中是只读的:

@property (nonatomic, strong) XMGStatus *status; /**< 微博模型 */

@property (nonatomic, assign, readonly) CGRect icon_Ima_frame; /**< 头像frame */

@property (nonatomic, assign, readonly) CGRect name_Lab_frame; /**< 昵称frame */

@property (nonatomic, assign, readonly) CGRect vip_Ima_frame; /**< vipframe */

@property (nonatomic, assign, readonly) CGRect text_Lab_frame; /**< 正文frame */

@property (nonatomic, assign, readonly) CGRect picture_Ima_frame; /**< 配图frame */

@property (nonatomic, assign, readonly) CGFloat cellH; /**< cell的高度 */

- 在status模型的set方法中实现只读属性的计算

#import "XMGStatusFrame.h"

@interface XMGStatusFrame ()

@property (nonatomic, assign) CGRect icon_Ima_frame; /**< 头像frame */

@property (nonatomic, assign) CGRect name_Lab_frame; /**< 昵称frame */

@property (nonatomic, assign) CGRect vip_Ima_frame; /**< vipframe */

@property (nonatomic, assign) CGRect text_Lab_frame; /**< 正文frame */

@property (nonatomic, assign) CGRect picture_Ima_frame; /**< 配图frame */

@property (nonatomic, assign) CGFloat cellH; /**< cell的高度 */

@end

@implementation XMGStatusFrame

- (void)setStatus:(XMGStatus *)status

{

_status = status;

CGFloat margin = 10;

1.头像:宽高都是30,距离top.left都是10

CGFloat iconX = margin;

CGFloat iconY = margin;

CGFloat iconW = 30;

CGFloat iconH = iconW;

CGRect icon_Ima_frame = CGRectMake(iconX, iconY, iconW, iconH);

self.icon_Ima_frame = icon_Ima_frame;

2.昵称:left距离icon_Ima为10,顶部和icon_Ima对齐,宽高根据文字大小决定

CGFloat nameX = CGRectGetMaxX(icon_Ima_frame) + margin;

CGFloat nameY = iconY;

根据字体大小计算单行文字的size

CGSize nameSize = [status.name sizeWithAttributes:@{NSFontAttributeName: NameFont}];

CGFloat nameW = nameSize.width;

CGFloat nameH = nameSize.height;

CGRect name_Lab_frame = CGRectMake(nameX, nameY, nameW, nameH);

self.name_Lab_frame = name_Lab_frame;

3.vip:宽高14,距离昵称10,垂直方向与昵称中线对齐

CGFloat vipX = CGRectGetMaxX(name_Lab_frame) + margin;

CGFloat vipW = 14;

CGFloat vipH = 14;

CGFloat vipY = CGRectGetMidY(name_Lab_frame) - vipH * 0.5; // CGRectGetMidY(self.name_Lab.frame) = vipY + vipH * 0.5

CGRect vip_Ima_frame = CGRectMake(vipX, vipY, vipW, vipH);

self.vip_Ima_frame = vip_Ima_frame;

4.正文:左边与头像对齐,右边距离与左边距离相同,顶部距离头像间距10,高度根据文字多少决定

CGFloat textX = iconX;

CGFloat textY = margin + CGRectGetMaxY(icon_Ima_frame);

CGFloat textW = [UIScreen mainScreen].bounds.size.width - 2 * textX;

CGSize maxSize = CGSizeMake(textW, CGFLOAT_MAX);

CGSize textSize = [status.text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: TextFont} context:nil].size;

CGFloat textH = textSize.height;

CGRect text_Lab_frame = CGRectMake(textX, textY, textW, textH);

self.text_Lab_frame = text_Lab_frame;

5.配图:宽高100,左边与正文对齐,顶部距离正文距离为10

CGRect picture_Ima_frame = CGRectZero;

if (status.picture) {

CGFloat pictureW = 100;

CGFloat pictureH = 100;

CGFloat pictureX = textX;

CGFloat pictureY = margin + CGRectGetMaxY(text_Lab_frame);

picture_Ima_frame = CGRectMake(pictureX, pictureY, pictureW, pictureH);

}

self.picture_Ima_frame = picture_Ima_frame;

self.cellH = (status.picture)? CGRectGetMaxY(picture_Ima_frame) + margin: CGRectGetMaxY(text_Lab_frame) + margin;

}

- 删除XMGStatus中的多余属性,完善模型属性set方法

- (void)setIcon:(UIImage *)icon

{

if ([icon isKindOfClass:[NSString class]]) {

_icon = [UIImage imageNamed:(NSString *)icon];

}else

{

_icon = icon;

}

}

- (void)setVip:(id)vip

{

if ([vip isKindOfClass:[NSNumber class]]) {

_vip = [vip boolValue];

}else

{

_vip = vip;

}

}

- (void)setPicture:(UIImage *)picture

{

if ([picture isKindOfClass:[NSString class]]) {

_picture = (picture)? [UIImage imageNamed:(NSString *)picture]: nil;

}else

{

_picture = picture;

}

}

- 修改控制器中的模型数组为 statusFrames

懒加载

- (NSArray *)statusFrames

{

if (!_statusFrames) {

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil];

NSArray *dicts = [NSArray arrayWithContentsOfFile:filePath];

NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:dicts.count];

for (NSDictionary *dict in dicts) {

XMGStatus *obj = [XMGStatus statusWithDict:dict];

转换代码如下

XMGStatusFrame *statusFrame = [[XMGStatusFrame alloc] init];

statusFrame.status = obj;

[arrayM addObject:statusFrame];

}

_statusFrames = [arrayM copy];

}

return _statusFrames;

}

- 修改cell中的模型属性为statusFrame

2.在模型的set方法中为子控件赋值数据

- (void)setStatusFrame:(XMGStatusFrame *)statusFrame

{

_statusFrame = statusFrame;

XMGStatus *status = statusFrame.status;

1.头像

self.icon_Ima.image = status.icon;

2.昵称

self.name_Lab.text = status.name;

self.name_Lab.textColor = status.isVip ? [UIColor orangeColor]: [UIColor blackColor];

3.vip

self.vip_Ima.hidden = !status.isVip;

4.正文

self.text_Lab.text = status.text;

5.配图

if (status.picture) {

self.picture_Ima.hidden = NO;

self.picture_Ima.image = status.picture;

}else

{

self.picture_Ima.hidden = YES;

self.picture_Ima.image = nil;

}

可以将frame在模型的方法中提前设置好,这样不用重写layoutSubviews方法了

self.icon_Ima.frame = statusFrame.icon_Ima_frame;

self.name_Lab.frame = statusFrame.name_Lab_frame;

self.vip_Ima.frame = statusFrame.vip_Ima_frame;

self.text_Lab.frame = statusFrame.text_Lab_frame;

self.picture_Ima.frame = statusFrame.picture_Ima_frame;

}

- 完善数据源代理方法中的赋值

#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

return self.statusFrames.count;

}

在cell显示到屏幕上之前,cell的高度就已经被确定

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

拿到cell

XMGStatusCell *cell = [XMGStatusCell cellWithTableView:tableView];

覆盖数据

cell.statusFrame = self.statusFrames[indexPath.row];

return cell;

}

pragma mark - UITableViewDelegate

warning 当tableView加载的时候,在cell显示到屏幕上之前(cell创建之前),tableView会先拿到所有的cell的高度,来确定contentSize用来估算右边索引条的高度,所以在创建cell的时候,一定是要知道cell高度的

根据indexPath返回cell的高度

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

{

XMGStatusFrame *statusFrame = self.statusFrames[indexPath.row];

return statusFrame.cellH;

}

不等高cell-Autolayout-iOS8+

了解估算高度

- 多添加一个最后一个子控件距离contentView底部的约束

- 在viewDidLoad方法中

先设置估算高度,tableView刚开始显示的滚动条就可以根据估算高度得到contentSize

self.tableView.estimatedRowHeight = 250;

取消storyboard中行高的设置

self.tableView.rowHeight = UITableViewAutomaticDimension;

- 在有配图的情况下,拖线设置新添加约束的值即可

微博正文距离底部的约束

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textToBottom;

5.配图

if (status.picture) {

self.picture_Ima.hidden = NO;

self.picture_Ima.image = status.picture;

self.textToBottom.constant = 120;

}else

{

self.picture_Ima.hidden = YES;

self.picture_Ima.image = nil;

self.textToBottom.constant = 10;

}

不等高cell-Autolayout-iOS6,7

首先恢复高度250的等高cell

- 去掉最后底部的约束以及代码

- 去掉self-sizing的两行代码

先设置估算高度,tableView刚开始显示的滚动条就可以根据估算高度得到contentSize

self.tableView.estimatedRowHeight = 250;

行高是由约束自动决定是多少

self.tableView.rowHeight = UITableViewAutomaticDimension;

分析方法调用顺序

- 再次启用代理方法 heightForRowAtIndexPath

* 创建一个临时的 tempCell

* 强制布局

* 解决依赖于屏幕宽度的 text_Lab

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

{

要在此处得到显示在屏幕上的cell的高度

拿到cell

XMGStatusCell *tempCell = [XMGStatusCell cellWithTableView:tableView];

覆盖数据

tempCell.status = self.statuses[indexPath.row];

需要在布局前,对我们多行cell显示的最大宽度设置限制

[tempCell.text_Lab setPreferredMaxLayoutWidth:tableView.bounds.size.width - 20];

warning 由于我们创建的tempCell不是显示到屏幕上的,所以父view没有宽度,导致原先的约束失效,我们的Autolayout没有布局成frame

强制布局

[tempCell layoutIfNeeded];

[tempCell.text_Lab setPreferredMaxLayoutWidth:tableView.bounds.size.width - 20];

[tempCell layoutIfNeeded];

CGFloat cellH = (tempCell.status.picture) ? CGRectGetMaxY(tempCell.picture_Ima.frame) :CGRectGetMaxY(tempCell.text_Lab.frame);

cellH += 10;

return cellH;

}

- 分析总结:

* cell.text_Lab.宽度约束 = cell.contentView的宽度 - 20

* 由于我们创建的tempCell不显示到屏幕上的,所以cell.contentView宽度不确定,导致原先的约束无效,我们的Autolayout没有布局成frame

* 解决办法(2种):

* 1.设置text_Lab的最大宽度

[tempCell.text_Lab setPreferredMaxLayoutWidth:tableView.bounds.size.width - 20];

* 2.为text_Lab的宽度约束拖一条线手动设置

手动设置了有效的,不依赖于父view的约束

self.textW.constant = [UIScreen mainScreen].bounds.size.width - 20;

- 性能优化

* heightForRowAtIndexPath`调用频繁

* tempCell 只需要创建一次

* 封装cellH 的计算

* 设置估算高度 self.tableView.estimatedRowHeight = 250;

XMGStatusCell *tempCell;

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

{

要在此处得到显示在屏幕上的cell的高度

拿到cell

if (!tempCell) {

tempCell = [XMGStatusCell cellWithTableView:tableView];

NSLog(@"%p, %s, line = %d", tempCell, __FUNCTION__, __LINE__);

}

覆盖数据

tempCell.status = self.statuses[indexPath.row];

return tempCell.cellH;

}

- 估算高度的作用:让 heightForRowAtIndexPath 懒加载

- 代理方法

tableView用估算高度快速计算估算值来节省加载时间,如果这些方法实现,那我们显示在屏幕上的cell才会调用cellForRow

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 estimatedHeightForFooterInSection:(NSInteger)section NS_AVAILABLE_IOS(7_0);

纯代码计算不等高cell相关推荐

  1. ios15使用纯代码计算cell的高度

    ios15使用纯代码计算cell的高度 #import "MTableViewController.h" #import "MTableViewCell.h" ...

  2. 《uni-app》移动端纯CSS实现不等高的瀑布流效果

    <uni-app>移动端纯CSS实现不等高的瀑布流效果 前言 示例 WEB端示例 移动端示例 瀑布流实现 第一种: flex 核心代码 实现 缺点 完整代码 第二种:column-coun ...

  3. (素材源码)猫猫学IOS(十七)UI之纯代码自定义Cell实现新浪微博UI

    猫猫分享,必须精品 素材代码地址:http://download.csdn.net/detail/u013357243/8580249 原文地址:http://blog.csdn.net/u01335 ...

  4. 猫猫学IOS(十七)UI之纯代码自定义Cell实现新浪微博UI

    猫猫分享,必须精品 素材代码地址:http://blog.csdn.net/u013357243/article/details/44976175 原文地址:http://blog.csdn.net/ ...

  5. iOS开发UI篇—以微博界面为例使用纯代码自定义cell程序编码全过程(三·完结)...

    一.需要改进的地方 还需改进的地方:cell的高度需要根据每条微博的数据进行动态设置. 设置cell的高度可以有两种方式,一种是通过rowheight属性来进行设置,一种是通过代理来进行设置.通过属性 ...

  6. 自定义非等高 Cell

    1.自定义非等高 Cell介绍 1.1 代码自定义(frame) 新建一个继承自 UITableViewCell 的类. 重写 initWithStyle:reuseIdentifier: 方法. 添 ...

  7. 非等高cell实战(01)-- 实现微博页面

    非等高cell实战(01)-- 实现微博页面 学习过UITableView.AutoLayout以及MVC的相关知识,接下来通过一个微博页面实战来整合一下. 首先看一下效果图: 需求分析 此页面为非等 ...

  8. 史上比较用心的纯代码实现 AutoLayout

    入职有两三个月了吧,都是使用 Objective-C 纯代码(虽然有时候偷偷参杂一些 Swift 开源库)来编写公司APP,写布局的时候几乎都是要么在初始化的时候用 initWithFrame,要么就 ...

  9. 说一下StoreBoard和纯代码编程各有什么好处吧

    首先先说一下,本人是个纯代码党,喜欢敲击的快感!!! 先说一下StoreBoard吧 1StoreBoard 效率高; Auto Layout,做适配很方便; 多语言很方便; 静态TableView, ...

最新文章

  1. Python 程序员常见错误
  2. buu 权限获得第一步
  3. 自定义ClassLoader
  4. 字符串格式参数的日期比较函数
  5. 高性能 TCP UDP 通信框架 HP-Socket v3.3.1
  6. [疑难解答]MSN常见问题及回答(转)
  7. vue 富文本编辑器 Editor 使用
  8. AD7705/TM7705使用注意事项
  9. 银行卡四要素API 方便好用
  10. 首届IBC“社会影响力奖”表彰行业多元化、可持续发展和伦理领导
  11. FFmpeg —— ffplay源码 - 制作桌面动态壁纸
  12. Blender_8_内插面
  13. misrosoft word (受保护的视图)已停止工作问题
  14. 如何清洁你脏兮兮的笔记本电脑?
  15. JMX学习笔记(四) JMX RMI
  16. uniapp入门学习
  17. 关于计算机优点缺点的英语作文,关于网络优缺点的英语作文(精选3篇)
  18. 虚拟化服务器需要好显卡吗,虚拟化下的显卡
  19. ​目标检测算法——YOLOv5/YOLOv7改进之结合​SOCA(单幅图像超分辨率)
  20. c语言 遍历文件夹中所有文件名,C# 遍历文件夹下所有子文件夹中的文件,得到文件名...

热门文章

  1. Qt之使用QSS设置QPushButton图标和文本的位置
  2. js 按钮控制循环的开始和停止
  3. jstack命令应用
  4. iPad上的旋转问题
  5. C++实现简单绘图(类)
  6. Matplotlib——线图_plot()函数常见的参数列表
  7. html影院选座模板,jQuery在线选座(影院版)
  8. 并行计算模型有哪些?
  9. 长连接与短连接的区别以及使用场景
  10. 在虚拟机中(Hyper-V)中再建一个虚拟机(Hyper-v虚拟机嵌套)