首先附上Demo的源码下载地址:http://download.csdn.net/detail/liu537192/8468169
在ios-day07-01中我们使用的是xib来自定义cell,使用xib自定义cell适用于cell中的子控件个数一样且高度一致,如果cell中的子控件个数不一样且高度不一致时,我们就需要使用代码来自定义cell。
通过代码自定义cell(cell高度不一致)的步骤:
1,新建一个类继承自UITableViewCell
2,重写UITableViewCell的initWithStyle:reuseIdentifier:方法
2.1,添加所有需要显示的子控件,不需要设置子控件的数据和frame,子控件要添加到contentView中
2.2,进行子控件不变属性的赋值(有些属性只需要设置一次,比如字体、固定的图片等)
3,提供2个模型
3.1,数据模型:存放文字数据、图片数据
3.2,frame模型:存放3.1的数据模型、所有子控件的frame、cell的高度
4,自定义的cell中拥有一个frame模型(不要直接拥有数据模型,因为frame模型中包含了数据模型)
5,重写frame模型属性的setter方法:在这个方法中设置子控件的显示数据和子控件的frame
6,frame模型数据的初始化采用懒加载的方式(每一个cell对应的frame模型数据只会加载一次)
因为整个界面只有一个UITableView,所以我们可以使用UITableViewController。步骤如下:
1,让LiuJieViewController继承自UITableViewController,如下图所示
2,来到main.storyboard文件,删掉原来的控制器,弄一个UITableViewController,并设置其class为LiuJieViewController,如下图所示
3,这时,这个控制器的对应的view是一个UITableView,右键点击这个UITableView,发现UITableView的数据源和代理都已经自动设置好了,如下图所示
接下来,我们直接来看整个Demo的效果图:
核心代码:
//
// LiuJieViewController.m
// 02-微博
//
// Created by XinYou on 15-3-2.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "LiuJieViewController.h"
#import "LiuJieStatus.h"
#import "LiuJieStatusFrame.h"
#import "LiuJieStatusCell.h"
@interface LiuJieViewController ()
@property (nonatomic, strong) NSArray *statusFrames;
@end
@implementation LiuJieViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// self.tableView.rowHeight = 60;
}
- (BOOL)prefersStatusBarHidden{
return YES;
}
- (NSArray *)statusFrames{
if (_statusFrames == nil) {
// 获取plist文件的路径
NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil];
// 加载plist文件中的数据到数组
NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *tempArray = [NSMutableArray array];
// 字典对象转模型对象
for (NSDictionary *dict in dictArray) {
LiuJieStatus *status = [LiuJieStatus statusWithDict:dict];
LiuJieStatusFrame *statusFrame = [[LiuJieStatusFrame alloc] init];
statusFrame.status = status;
[tempArray addObject:statusFrame];
}
_statusFrames = tempArray;
}
return _statusFrames;
}
#pragma mark -数据源的方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.statusFrames.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
// 1,创建cell
LiuJieStatusCell *cell = [LiuJieStatusCell cellWithTableView:tableView];
// 2,给cell设置数据
cell.statusFrame = self.statusFrames[indexPath.row];
return cell;
}
#pragma mark -代理的方法
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
// 取出这行对应的frame模型
LiuJieStatusFrame *statusFrame = self.statusFrames[indexPath.row];
return statusFrame.cellHeight;
}
@end
//
// LiuJieStatus.h
// 02-微博
//
// Created by XinYou on 15-3-2.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@interface LiuJieStatus : NSObject
@property (nonatomic,copy) NSString *icon;// 头像
@property (nonatomic,copy) NSString *name;// 昵称
@property (nonatomic,copy) NSString *text;// 内容
@property (nonatomic,copy) NSString *picture;// 配图
@property (nonatomic,assign) BOOL vip;// 是否是vip会员
+ (instancetype)statusWithDict:(NSDictionary *)dict;
- (instancetype)initWithDict:(NSDictionary *)dict;
@end
//
// LiuJieStatus.m
// 02-微博
//
// Created by XinYou on 15-3-2.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "LiuJieStatus.h"
@implementation LiuJieStatus
- (instancetype)initWithDict:(NSDictionary *)dict{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
+ (instancetype)statusWithDict:(NSDictionary *)dict{
return [[self alloc] initWithDict:dict];
}
@end
//
// LiuJieStatusFrame.h
// 02-微博
//
// Created by XinYou on 15-3-2.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@class LiuJieStatus;
@interface LiuJieStatusFrame : NSObject
/**
* 头像的frame
* 这里为什么要用readOnly呢?因为各个子控件的frame是根据一定的规则计算出来的,
* 不能让别人随意的修改,使用readOnly的属性将只有get方法而没有set方法
*/
@property (nonatomic, assign, readonly) CGRect iconF;
/**
* 昵称的frame
*/
@property (nonatomic, assign, readonly) CGRect nameF;
/**
* vip会员图标的frame
*/
@property (nonatomic, assign, readonly) CGRect vipF;
/**
* 正文的frame
*/
@property (nonatomic, assign, readonly) CGRect textF;
/**
* 配图的frame
*/
@property (nonatomic, assign, readonly) CGRect pictureF;
/**
* cell的高度
*/
@property (nonatomic, assign, readonly) CGFloat cellHeight;
/**
* 各个子控件的frame需要根据LiuJieStatus来设置
*/
@property (nonatomic, strong) LiuJieStatus *status;
@end
//
// LiuJieStatusFrame.m
// 02-微博
//
// Created by XinYou on 15-3-2.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
// 昵称的字体
#define LiuJieNameFont [UIFont systemFontOfSize:14]
// 正文的字体
#define LiuJieTextFont [UIFont systemFontOfSize:15]
#import "LiuJieStatusFrame.h"
#import "LiuJieStatus.h"
@implementation LiuJieStatusFrame
/**
* 计算文本的尺寸
*
* @param text 文本内容
* @param font 文本字体
* @param maxSize 文本最大显示的尺寸
*
* @return 文本的尺寸
* (如果文本实际的尺寸没有达到maxSize,则返回文本的实际尺寸;如果文本实际尺寸超过了maxSize,则返回maxSize)
*/
- (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font maxSize:(CGSize)maxSize{
NSDictionary *attrs = @{NSFontAttributeName : font};
return [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
}
- (void)setStatus:(LiuJieStatus *)status{
// 固定写法
_status = status;
// 子控件之间的间距
CGFloat padding = 10;
// 1,头像
CGFloat iconX = padding;
CGFloat iconY = padding;
CGFloat iconW = 30;
CGFloat iconH = 30;
// 这里为什么不使用self.iconF?因为使用的readOnly,没有set方法
_iconF = CGRectMake(iconX, iconY, iconW, iconH);
// 2,昵称
// 计算昵称的尺寸
CGSize nameSize = [self sizeWithText:status.name font:LiuJieNameFont maxSize:CGSizeMake(MAXFLOAT, MAXFLOAT)];
// 昵称的X值 = 头像的最大的X值 + 间距
CGFloat nameX = CGRectGetMaxX(_iconF) + padding;
// 昵称在头像的右边,且昵称在Y方向相对于头像是居中的
CGFloat nameY = iconY + (iconH - nameSize.height) * 0.5;
_nameF = CGRectMake(nameX, nameY, nameSize.width, nameSize.height);
// 3,vip会员图标
CGFloat vipX = CGRectGetMaxX(_nameF) + padding;
CGFloat vipY = nameY;
// 根据图片的像素来确定
CGFloat vipW = 14;
CGFloat vipH = 14;
_vipF = CGRectMake(vipX, vipY, vipW, vipH);
// 4,正文
CGFloat textX = iconX;
CGFloat textY = CGRectGetMaxY(_iconF) + padding;
// 计算正文的尺寸
CGSize textSize = [self sizeWithText:status.text font:LiuJieTextFont maxSize:CGSizeMake(300, MAXFLOAT)];
CGFloat textW = textSize.width;
CGFloat textH = textSize.height;
_textF = CGRectMake(textX, textY, textW, textH);
// 5,配图
if (self.status.picture) {// 如果有配图
CGFloat pictureX = textX;
CGFloat pictureY = CGRectGetMaxY(_textF) + padding;
CGFloat pictureW = 100;
CGFloat pictureH = 100;
_pictureF = CGRectMake(pictureX, pictureY, pictureW, pictureH);
// 如果有配图,那么cell的高度就为配图的最大的Y值 + padding
_cellHeight = CGRectGetMaxY(_pictureF) + padding;
}else{
// 如果没有配图,那么cell的高度就为正文的最大的Y值 + padding
_cellHeight = CGRectGetMaxY(_textF) + padding;
}
}
@end
//
// LiuJieStatusCell.h
// 02-微博
//
// Created by XinYou on 15-3-2.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@class LiuJieStatusFrame;
@interface LiuJieStatusCell : UITableViewCell
/**
* 提供一个静态方法返回cell
*/
+ (instancetype)cellWithTableView:(UITableView *)tableView;
/**
* 要根据statusFrame中的数据来设置cell中子控件的数据
*/
@property (nonatomic,strong)LiuJieStatusFrame *statusFrame;
@end
//
// LiuJieStatusCell.m
// 02-微博
//
// Created by XinYou on 15-3-2.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
// 昵称的字体
#define LiuJieNameFont [UIFont systemFontOfSize:14]
// 正文的字体
#define LiuJieTextFont [UIFont systemFontOfSize:15]
#import "LiuJieStatusCell.h"
#import "LiuJieStatus.h"
#import "LiuJieStatusFrame.h"
@interface LiuJieStatusCell()
/**
* 头像
*/
@property (nonatomic,weak) UIImageView *iconView;
/**
* 昵称
*/
@property (nonatomic,weak) UILabel *nameView;
/**
* 会员图标
*/
@property (nonatomic,weak) UIImageView *vipView;
/**
* 正文
*/
@property (nonatomic,weak) UILabel *textView;
/**
* 配图
*/
@property (nonatomic,weak) UIImageView *pictureView;
@end
@implementation LiuJieStatusCell
/**
* 构造方法,在初始化对象的时候会调用
* 一般在这个方法中添加需要显示的子控件
* 在这个方法中,我们只初始化控件,设置控件的不变属性,其他属性不设置,数据也不设置
*/
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// 1,设置头像
UIImageView *iconView = [[UIImageView alloc] init];
// 注意这里是添加到cell的contentView中
[self.contentView addSubview:iconView];
self.iconView = iconView;
// 2,设置昵称
UILabel *nameView = [[UILabel alloc] init];
// 因为字体只需设置一次,属于不变的属性,所以在这里设置
nameView.font = LiuJieNameFont;
[self.contentView addSubview:nameView];
self.nameView = nameView;
// 3,设置vip会员图标
UIImageView *vipView = [[UIImageView alloc] init];
// 因为会员图标都是一样的,属于不变的属性
// 我们在后面的代码中再控制会员图标的显示(如果是会员就显示,不是会员就隐藏)
vipView.image = [UIImage imageNamed:@"vip"];
[self.contentView addSubview:vipView];
self.vipView = vipView;
// 4,设置正文
UILabel *textView = [[UILabel alloc] init];
// 设置UILabel有多少行,如果设置为0,则文本的总长度决定行数
textView.numberOfLines = 0;
// 设置字体
textView.font = LiuJieTextFont;
[self.contentView addSubview:textView];
self.textView = textView;
// 5,设置配图
UIImageView *prictureView = [[UIImageView alloc] init];
[self.contentView addSubview:prictureView];
self.pictureView = prictureView;
}
return self;
// 由上面的代码可以看出,在这个方法中我们初始化了要显示的子控件,
// 但是并没有设置子控件的尺寸
}
- (void)setStatusFrame:(LiuJieStatusFrame *)statusFrame{
// 固定写法
_statusFrame = statusFrame;
// 1,设置cell中各个子控件的数据
[self settingData];
// 2,设置cell中各个子控件的frame
[self settingFrame];
}
/**
* 设置cell中各个子控件的数据
*/
- (void)settingData{
// 微博数据
LiuJieStatus *status = self.statusFrame.status;
// 1,设置头像
self.iconView.image = [UIImage imageNamed:status.icon];
// 2,设置昵称
self.nameView.text = status.name;
// 3,设置vip会员图标的显示和隐藏
if (status.vip) {// 如果是vip
// 显示vip图标
self.vipView.hidden = NO;
// 昵称为红色
self.nameView.textColor = [UIColor redColor];
}else{// 如果不是vip
// 隐藏vip图标
self.vipView.hidden = YES;
// 昵称为黑色
self.nameView.textColor = [UIColor blackColor];
}
// 4,设置正文
self.textView.text = status.text;
// 5,设置配图
if (status.picture) {// 有配图
self.pictureView.hidden = NO;
self.pictureView.image = [UIImage imageNamed:status.picture];
}else{// 没有配图
self.pictureView.hidden = YES;
}
}
/**
* 设置cell中各个子控件的尺寸
*/
- (void)settingFrame{
// 1,头像
self.iconView.frame = self.statusFrame.iconF;
// 2,昵称
self.nameView.frame = self.statusFrame.nameF;
// 3,会员图标
self.vipView.frame = self.statusFrame.vipF;
// 4,正文
self.textView.frame = self.statusFrame.textF;
// 5,配图
if (self.statusFrame.status.picture) {// 有配图
self.pictureView.frame = self.statusFrame.pictureF;
}
}
+ (instancetype)cellWithTableView:(UITableView *)tableView{
static NSString *ID = @"status";
LiuJieStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[LiuJieStatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
}
@end