不等高Cell

不等高cell纯代码:

不等高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_Ima10,顶部和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_Ima10,顶部和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_Ima10,顶部和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); 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值