iOS 纯代码实现自定义Cell
2018-05-23 本文已影响165人
奋拓达
自适应TableViewCell高度计算优化方案:
1、TableView自定义控件的步骤:
1. 重写initWithFrame、awakeFromNib等初始化方法
2. 重写LayoutSubView方法布局子控件的frame
3. 为cell添加一个模型属性,并实现setModel方法,用于给子控件赋值
4. 提供一个便利的初始化方法用来快速初始化自定义的控件
2、计算UILabel的高度及尺寸
1、计算UILabel的尺寸是和字体相关的,不同的字体类型,字体大小 这些因素都会影响UILabel的宽度,一次计算尺寸的时候字体参数是必须的。
2、计算单行的UILabel:sizeWithAttributes:@{NSFontAttributeName:[UIFont systemOfSize:17.f]}
3、计算多行的Label:和计算单行Label的方法不同,因为是多行需要计算UILabel究竟有多宽换行,同样的内容长度,宽度越宽,高度就越低,所以必须制定需要换行的最大的约束尺寸,高度是指在多宽的时候换行,高度是指实际计算后所返回的最大高度,如果设置的值比实际的值要小,只能返回所设置值大小,所以这个值一般都写很大的一个值,可以使用一个常量MAXFloat表示。
boundingRectWithSize:options:attributes:context;
4、rowHeight属性是UITableView的属性,而不是UITableViewCell的属性
3、调用顺序
- viewDidLoad
- tableView:numberOfRowsInSection:
- tableView:heightForRowAtIndexPath: // 该方法一直循环调用N次,因初始化控件时需要知道contentSize的尺寸,只有知道所有cell的高度,才能计算总高度,根据contentSize的高度也能确定滚动条的高度
- tableView:cellForRowAtIndexPath: // 该方法一直循环调用N次
- initWithStyle:reuseIdentifier:
- setModel:
- layoutSubviews // 该方法一直循环调用N次需要注意的是自定义cell是先设置模型数据,后调用layoutSubviews布局子视图
模型类的设置
#import <Foundation/Foundation.h>
#import "CellFrame.h"
@interface Status : NSObject
@property (copy, nonatomic) NSString *icon;
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic, getter=isVip) BOOL vip;
@property (copy, nonatomic) NSString *text;
@property (copy, nonatomic) NSString *picture;
@property (strong, nonatomic) CellFrame *cellFrame;
@end
//-----------------------------------------
#import "Status.h"
@implementation Status
- (CellFrame *)cellFrame {
if (_cellFrame == nil) {
_cellFrame = [[CellFrame alloc] initWithStatus:self];
}
return _cellFrame;
}
@end
对CellFrame的计算
#import <UIKit/UIKit.h>
@class Status;
@interface CellFrame : NSObject
@property (assign, nonatomic) CGRect headFrame;
@property (assign, nonatomic) CGRect nicknameFrame;
@property (assign, nonatomic) CGRect vipFrame;
@property (assign, nonatomic) CGRect contentFrame;
@property (assign, nonatomic) CGRect pictureFrame;
@property (assign, nonatomic) CGFloat cellHeight;
- (instancetype)initWithStatus:(Status *)status;
@end
//------------------------------------------
#import "CellFrame.h"
#import "Status.h"
@interface CellFrame ()
@property (weak, nonatomic) Status *status;
@end
@implementation CellFrame
- (instancetype)initWithStatus:(Status *)status {
if (self = [super init]) {
_status = status;
}
return self;
}
- (CGFloat)cellHeight {
if (_cellHeight == 0) {
// head frame
CGFloat margin = 10;
self.headFrame = CGRectMake(margin, margin, 50, 50);
// nickename frame
CGFloat nickenameLabelX = CGRectGetMaxX(self.headFrame) + margin;
CGFloat nickenameLabelY = CGRectGetMinY(self.headFrame);
CGSize nickenameLabelSize = [self.status.name sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f]}];
self.nicknameFrame = CGRectMake(nickenameLabelX, nickenameLabelY, nickenameLabelSize.width, nickenameLabelSize.height);
// vip frame
if (self.status.isVip) {
CGFloat vipImageViewX = CGRectGetMaxX(self.nicknameFrame) + margin;
CGFloat vipImageViewY = CGRectGetMinY(self.nicknameFrame);
self.vipFrame = CGRectMake(vipImageViewX, vipImageViewY, 14, 14);
}
// content frame
CGFloat contentLabelX = CGRectGetMinX(self.headFrame);
CGFloat contentLabelY = CGRectGetMaxY(self.headFrame) + margin;
CGRect contentRect = [self.status.text boundingRectWithSize:CGSizeMake([UIScreen mainScreen].bounds.size.width - 2 * margin, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14.f]} context:nil];
self.contentFrame = CGRectMake(contentLabelX, contentLabelY, contentRect.size.width, contentRect.size.height);
// picture frame
if (self.status.picture != nil) {
CGFloat pictureLabelX = CGRectGetMinX(self.contentFrame);
CGFloat pictureLabelY = CGRectGetMaxY(self.contentFrame) + margin;
self.pictureFrame = CGRectMake(pictureLabelX, pictureLabelY, 70, 100);
self.cellHeight = CGRectGetMaxY(self.pictureFrame) + margin;
} else {
self.cellHeight = CGRectGetMaxY(self.contentFrame) + margin;
}
}
return _cellHeight;
}
@end
自定义Cell
#import <UIKit/UIKit.h>
#import "Status.h"
@interface TableViewCell : UITableViewCell
@property (strong, nonatomic) Status *status;
@end
#import "TableViewCell.h"
@interface TableViewCell ()
@property (weak, nonatomic) UIImageView *headImageView;
@property (weak, nonatomic) UILabel *nicknameLabel;
@property (weak, nonatomic) UIImageView *vipImageView;
@property (weak, nonatomic) UILabel *contentLabel;
@property (weak, nonatomic) UIImageView *pictureImageView;
@end
@implementation TableViewCell
// 1. 初始化子视图
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// 头像
UIImageView *headImageView = [[UIImageView alloc] init];
[self.contentView addSubview:headImageView];
self.headImageView = headImageView;
// 昵称
UILabel *nicknameLabel = [[UILabel alloc] init];
nicknameLabel.font = [UIFont systemFontOfSize:17.f];
[self.contentView addSubview:nicknameLabel];
self.nicknameLabel = nicknameLabel;
// 图标
UIImageView *vipImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"vip"]];
[self.contentView addSubview:vipImageView];
self.vipImageView = vipImageView;
// 正文
UILabel *contentLabel = [[UILabel alloc] init];
contentLabel.numberOfLines = 0;
contentLabel.font = [UIFont systemFontOfSize:14.f];
[self.contentView addSubview:contentLabel];
self.contentLabel = contentLabel;
// 配图
UIImageView *pictureImageView = [[UIImageView alloc] init];
[self.contentView addSubview:pictureImageView];
self.pictureImageView = pictureImageView;
}
return self;
}
// 2. 布局子视图
- (void)layoutSubviews {
[super layoutSubviews];
self.headImageView.frame = self.status.cellFrame.headFrame;
self.nicknameLabel.frame = self.status.cellFrame.nicknameFrame;
self.vipImageView.frame = self.status.cellFrame.vipFrame;
self.contentLabel.frame = self.status.cellFrame.contentFrame;
self.pictureImageView.frame = self.status.cellFrame.pictureFrame;
}
// 3. 填充数据
- (void)setStatus:(Status *)status {
_status = status;
// head
self.headImageView.image = [UIImage imageNamed:status.icon];
// nickname & vip
self.nicknameLabel.text = status.name;
if (status.isVip) {
self.nicknameLabel.textColor = [UIColor orangeColor];
self.vipImageView.hidden = NO;
} else {
self.nicknameLabel.textColor = [UIColor blackColor];
self.vipImageView.hidden = YES;
}
// content
self.contentLabel.text = status.text;
// picture
if (status.picture != nil) {
self.pictureImageView.hidden = NO;
self.pictureImageView.image = [UIImage imageNamed:status.picture];
} else {
self.pictureImageView.hidden = YES;
}
}
@end
如何使用自定义的TableViewCell
#import <UIKit/UIKit.h>
@interface ViewController : UITableViewController
@end
#import "ViewController.h"
#import "TableViewCell.h"
#import "MJExtension.h"
@interface ViewController ()
@property (strong, nonatomic) NSArray *statuses;
@end
@implementation ViewController
static NSString *ID = @"ViewController";
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[TableViewCell class] forCellReuseIdentifier:ID];
self.tableView.estimatedRowHeight = 200;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.statuses.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"%s", __func__);
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
cell.status = self.statuses[indexPath.row];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"%s", __func__);
Status *status = self.statuses[indexPath.row];
return status.cellFrame.cellHeight;
}
/**懒加载 */
- (NSArray *)statuses {
if (_statuses == nil) {
_statuses = [Status mj_objectArrayWithFilename:@"statuses.plist"];
}
return _statuses;
}
@end