复杂cell的高度计算
- 实现了多行文字的自适应
- 实现了图文混排的自适应
在我的日常开发中经常会遇到cell内容比较复杂的情况,复杂的cell势必会有cell高度不相同的情况,这种需求往往是比较蛋疼的。但是没关系只要掌握了其中的原理剩下的就只是体力活了。本文采用传统方式纯代码布局来计算cell的高度值,如果精力允许下篇介绍AutoLayout自动布局下的cell高度的计算。
ViewController中的主要代码实现
为了方便起见本ViewController继承自UITableViewController具体实现代码如下:
//
// MTableViewController.m
// cell计算
//
// Created by code_xq on 16/3/12.
// Copyright © 2016年 code_xq. All rights reserved.
//
#import "MTableViewController.h"
#import "MTableViewCell.h"
#import "DataModel.h"
static NSString *ID = @"cell";
@interface MTableViewController ()
@property (nonatomic, strong) NSMutableArray *dataSource;
@end
@implementation MTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"cell的高度计算";
// 去除tableView的默认下划线
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundColor = [UIColor colorWithWhite:0.9 alpha:0.9];
// 注册cell
[self.tableView registerClass:[MTableViewCell class] forCellReuseIdentifier:ID];
// 异步获取数据
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *path = [[NSBundle mainBundle] pathForResource:@"cellList.plist" ofType:nil];
NSArray *array = [NSArray arrayWithContentsOfFile:path];
for (NSDictionary *dict in array) {
DataModel *dm = [DataModel initWith:dict];
[self.dataSource addObject:dm];
}
// 造数据
[self.dataSource addObjectsFromArray:self.dataSource];
// 在主线程中刷新数据
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataSource.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.dataModel = self.dataSource[indexPath.row];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
DataModel *dm = self.dataSource[indexPath.row];
return dm.cellHeight;
}
/**
* 给出cell的估计高度,主要目的是优化cell高度的计算次数
*/
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 200;
}
/**
* 初始化数据
*/
- (NSMutableArray *)dataSource {
if (_dataSource == nil) {
_dataSource = [NSMutableArray array];
}
return _dataSource;
}
@end
这段代码是初始化tableView的常规做法,值得注意的是UITableView的
estimatedHeightForRowAtIndexPath 这个方法给出cell的预估值,如果没有实现这个方法,tableView内部会一次性将所有的cell的高度全部计算出来,有了这个方法会对tableView的性能有所提高,这个方法的返回值理论上可以是任意值。
Model数据的代码实现
//
// DataModel.h
// cell计算
//
// Created by code_xq on 16/3/12.
// Copyright © 2016年 code_xq. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface DataModel : NSObject
/** 文字内容 */
@property (nonatomic, copy) NSString *text;
/** 图标*/
@property (nonatomic, copy) NSString *icon;
/** 图片*/
@property (nonatomic, copy) NSString *picture;
/** 用户名*/
@property (nonatomic, strong) NSString *name;
/** cell的高度*/
@property (nonatomic, assign) CGFloat cellHeight;
+ (instancetype)initWith:(NSDictionary *)dict;
@end
/***********************类的实现*********************/
#import "DataModel.h"
@implementation DataModel
+ (instancetype)initWith:(NSDictionary *)dict {
DataModel *dm = [[self alloc] init];
[dm setValuesForKeysWithDictionary:dict];
return dm;
}
@end
数据是从plist中获取直接转换成DataModel对象的,这里多了一个cellHeight的属性用来存放cell的高度。
重头戏自定义cell的代码实现
//
// MTableViewCell.h
// cell计算
//
// Created by code_xq on 16/3/12.
// Copyright © 2016年 code_xq. All rights reserved.
//
#import <UIKit/UIKit.h>
@class DataModel;
@interface MTableViewCell : UITableViewCell
/** 数据模型*/
@property (nonatomic, strong) DataModel *dataModel;
@end
/***********************类的实现*********************/
#import "MTableViewCell.h"
#import "UIView+Expand.h"
#import "DataModel.h"
#define SCWIDTH [UIScreen mainScreen].bounds.size.width
#define SCHEIGHT [UIScreen mainScreen].bounds.size.height
static CGFloat const margin = 10;
@interface MTableViewCell()
@property (nonatomic, weak) UIImageView *imageIcon;
@property (nonatomic, weak) UILabel *labelName;
@property (nonatomic, weak) UILabel *labelContent;
@property (nonatomic, weak) UIImageView *picView;
@end
@implementation MTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setUpView];
}
return self;
}
- (void)setUpView {
// 用户头像
UIImageView *imageIcon = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
imageIcon.left = margin;
imageIcon.top = margin;
self.imageIcon = imageIcon;
[self.contentView addSubview:imageIcon];
// 用户名
CGFloat nameW = SCWIDTH - imageIcon.width - 3 * margin;
UILabel *labelName = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, nameW, 30)];
labelName.left = imageIcon.right + margin;
labelName.top = margin;
labelName.font = [UIFont systemFontOfSize:20];
self.labelName = labelName;
[self.contentView addSubview:labelName];
// 文字内容
UILabel *labelContent = [[UILabel alloc] initWithFrame:CGRectMake(margin, 0, SCWIDTH - 20 , 30)];
labelContent.top = imageIcon.bottom + margin;
// 设置显示多行文字
labelContent.lineBreakMode = NSLineBreakByCharWrapping;
labelContent.numberOfLines = 0;
labelContent.font = [UIFont systemFontOfSize:15];
self.labelContent = labelContent;
[self.contentView addSubview:labelContent];
// 图片
UIImageView *picView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 120, 90)];
picView.left = margin;
self.picView = picView;
[self.contentView addSubview:picView];
}
- (void)setFrame:(CGRect)frame {
frame = CGRectMake(frame.origin.x, frame.origin.y + 10, frame.size.width, frame.size.height - 10);
[super setFrame:frame];
}
- (void)setDataModel:(DataModel *)dataModel {
_dataModel = dataModel;
// 设置用户头像
self.imageIcon.image = [UIImage imageNamed: dataModel.icon];
// 设置用户名
self.labelName.text = dataModel.name;
// 计算文字内容的高度
CGFloat height = [dataModel.text boundingRectWithSize:CGSizeMake(SCWIDTH - 2 * margin, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName : self.labelContent.font}
context:nil].size.height;
self.labelContent.height = height;
self.labelContent.text = dataModel.text;
// 设置图片内容
if (dataModel.picture) {
self.picView.hidden = NO;
self.picView.top = self.labelContent.bottom + margin;
self.picView.image = [UIImage imageNamed:dataModel.picture];
dataModel.cellHeight = self.picView.bottom + 2 * margin;
} else {
self.picView.hidden = YES;
dataModel.cellHeight = self.labelContent.bottom + 2 * margin;
}
}
@end
为了布局方便自己给UIView写了个分类可以很容易获取view的left、top、right、bottom的值,好了到此复杂cell的高度计算就讲完了,如果有机会继续讲述AutoLayout自动布局下的cell高度的计算。