- 方法一:
- 返回每行的真实高度,创建临时cell(不能够利用缓冲池重用优化),浪费大量内存
- heightForRowAtIndexPath:返回每一行的高度
- 此方法中调用cellForRowAtIndexPath会造成死循环(通过行号先获取cell,会先创建cell,在创建之前会先获取高度,又会调用当前方法…)
- cellForRowAtIndexPath:创建每一行cell
- 前的调用在创建之前,而且有多少行就会调用多少次,所以开始返回的高度都是0;
- 解决方法:临时创建cell,并给里面的控件赋值,强制布局,显示出来,返回该行真实高度.
- layoutIfNeeded:强制布局,当给控件赋值的时候,控件或许没有布局好,这时候的高度是不准确的,当强制布局后,控件真正显示出来,这时候是真实的高度
- heightForRowAtIndexPath:返回每一行的高度
- 返回每行的真实高度,创建临时cell(不能够利用缓冲池重用优化),浪费大量内存
/**
*返回每一行的高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
WQStatusCell *cell = [WQStatusCell statusWithTableView:tableView];
cell.statusData = self.statusData[indexPath.row];
[cell layoutIfNeeded ];
NSLog(@"%zd,%f",indexPath.row,cell.rowHeight);
return cell.rowHeight;
}
estimatedHeightForRowAtIndexPath:该函数返回估计的每行的高度,返回值可以给一个估计的固定的值
- 实现该代理方法后,会改变heightForRowAtIndexPath函数的调用顺序.
- 开始调用此方法,之后每调用cellForRowAtIndexPath创建一个cell后,紧接着调用heightForRowAtIndexPath方法
- heightForRowAtIndexPath取到的是布局后的cell,此时cell上控件没有布局好,所以取到的高度是不正确的.
方法二:
- 可以在自定义cell类中增加一个rowHeight方法,然后实现其getter方法.
- 在创建cell的时候先强制自动布局,然后获取后保存在一个可变数组中,返回高度
/** row高度*/
@property (nonatomic, assign) CGFloat rowHeight;
- (CGFloat )rowHeight{
if (self.pictureImageView.hidden) {
_rowHeight = CGRectGetMaxY(self.contentLabel.frame) + 10;
}else
{
_rowHeight = CGRectGetMaxY(self.pictureImageView.frame) + 10;
}
return _rowHeight;
}
/** 存放所有cell的高度 */
@property (strong, nonatomic) NSMutableDictionary *heights;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
XMGStatusCell *cell = [WQStatusCell cellWithTableView:tableView];
cell.status = self.statuses[indexPath.row];
// 强制布局
[cell layoutIfNeeded];
// 存储高度
self.heights[@(indexPath.row)] = @(cell.height);
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
XMGStatus *staus = self.statuses[indexPath.row];
return staus.cellHeight;
}
方法三:
- 在自定义数据模型类中增加一个cellHeight属性
- 在自定义cell类的数据模型setter方法中,先强制布局,然后获取cell的真实高度,赋值给数据模型的cellHeight.(控制器 和 cell类中的数据模型是指针传递,指向同一内存空间),同时控制器中模型数据数组中的rowHieght值也更新,
- 在控制器中先估计高度值,然后每创建一个cell,根据下标获取真实高度值
数据模型类:
#import <UIKit/UIKit.h>
@interface XMGStatus : NSObject
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *text;
@property (strong, nonatomic) NSString *icon;
@property (strong, nonatomic) NSString *picture;
@property (assign, nonatomic, getter=isVip) BOOL vip;
/** cell的高度 */
@property (assign, nonatomic) CGFloat cellHeight;
+ (instancetype)statusWithDict:(NSDictionary *)dict;
@end
#import "XMGStatus.h"
@implementation XMGStatus
+ (instancetype)statusWithDict:(NSDictionary *)dict
{
XMGStatus *status = [[self alloc] init];
[status setValuesForKeysWithDictionary:dict];
return status;
}
@end
- cell类
#import <UIKit/UIKit.h>
@class XMGStatus;
@interface XMGStatusCell : UITableViewCell
+ (instancetype)cellWithTableView:(UITableView *)tableView;
/** 微博模型数据 */
@property (nonatomic, strong) XMGStatus *status;
@end
#import "XMGStatusCell.h"
#import "XMGStatus.h"
@interface XMGStatusCell()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UIImageView *vipView;
@property (weak, nonatomic) IBOutlet UILabel *contentLabel;
@property (weak, nonatomic) IBOutlet UIImageView *pictureView;
@end
@implementation XMGStatusCell
+ (instancetype)cellWithTableView:(UITableView *)tableView
{
static NSString *ID = @"status";
XMGStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
return cell;
}
- (void)awakeFromNib
{
// 设置label每一行文字的最大宽度
// 为了保证计算出来的数值 跟 真正显示出来的效果 一致
self.contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
}
- (void)setStatus:(XMGStatus *)status
{
_status = status;
if (status.isVip) {
self.nameLabel.textColor = [UIColor orangeColor];
self.vipView.hidden = NO;
} else {
self.nameLabel.textColor = [UIColor blackColor];
self.vipView.hidden = YES;
}
self.nameLabel.text = status.name;
self.iconView.image = [UIImage imageNamed:status.icon];
if (status.picture) {
self.pictureView.hidden = NO;
self.pictureView.image = [UIImage imageNamed:status.picture];
} else {
self.pictureView.hidden = YES;
}
self.contentLabel.text = status.text;
// 强制布局
[self layoutIfNeeded];
// 计算cell的高度
if (self.pictureView.hidden) { // 没有配图
status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10;
} else { // 有配图
status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10;
}
}
@end
- 控制器类
#import <UIKit/UIKit.h>
@interface XMGStatusesViewController : UITableViewController
@end
#import "XMGStatusesViewController.h"
#import "XMGStatus.h"
#import "XMGStatusCell.h"
@interface XMGStatusesViewController ()
@property (strong, nonatomic) NSArray *statuses;
@end
@implementation XMGStatusesViewController
- (NSArray *)statuses
{
if (_statuses == nil) {
// 加载plist中的字典数组
NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil];
NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
// 字典数组 -> 模型数组
NSMutableArray *statusArray = [NSMutableArray array];
for (NSDictionary *dict in dictArray) {
XMGStatus *status = [XMGStatus statusWithDict:dict];
[statusArray addObject:status];
}
_statuses = statusArray;
}
return _statuses;
}
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.statuses.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
XMGStatusCell *cell = [XMGStatusCell cellWithTableView:tableView];
cell.status = self.statuses[indexPath.row];
return cell;
}
#pragma mark - 代理方法
/**
* 返回每一行的高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
XMGStatus *staus = self.statuses[indexPath.row];
return staus.cellHeight;
}
/**
* 返回每一行的估计高度
* 只要返回了估计高度,那么就会先调用tableView:cellForRowAtIndexPath:方法创建cell,再调用tableView:heightForRowAtIndexPath:方法获取cell的真实高度
*/
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 200;
}
@end
注意点:
- 当cell上又控件需要满足一定的条件隐藏和显示或者这改变颜色时,在不满足条件时需要改回去,
- 比如一个imageView用来显示qq空间动态配图,当一个用户没有配图时,该控件隐藏,当循环利用到另一行cell用来显示另一条动态,但是有配图,此时也不会显示,所以要先显示.
if (status.picture){ self.pictureView.hidden = NO; self.pictureView.image = [UIImage imageNamed:status.picture]; } else { self.pictureView.hidden = YES; }
- 当给cell里的控件设置数据或者设置约束需要取到控件的范围或者高度等值时,为了得到布局后显示在cell上的准确值,每次都需要通过layoutIfNeeded强制布局.
- 约束前会用到宽高度或者范围值要先强制更新
- 当约束后再取到高度或范围要重新强制布局
当label没有设置文字的最大宽度的时候,底部会显示一部分空隙,原因是每一行末尾有时因不能放下一个字或者单词而换行,造成计算误差.当设置每一行文字最大宽度后准确显示
// 设置label每一行文字的最大宽度 // 为了保证计算出来的数值 跟 真正显示出来的效果 一致 // 不设置可能因为单行末尾因为不足以显示一个字或者单词而换行造成误差,label底部空出一段高度 self.contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
- 当cell上又控件需要满足一定的条件隐藏和显示或者这改变颜色时,在不满足条件时需要改回去,