概述
我们要实现的效果:
这个界面布局也是UITableView实现的,其中的内容就是UITableViewCell,只是这个UITableViewCell是用户自定义实现的。虽然系统自带的UITableViewCell已经够强大了,但是这个界面布局并不能满足我们的需求。
在上面的cell布局里,我们可以知道,每个cell的高度都是不固定的。因此,我们通过代码来自定义cell.
代码自定义cell
步骤:
1.新建一个继承自UITableViewCell的类
2.重写initWithStyle:reuseIdentifier:方法
- 添加所有需要显示的子控件(不需要设置子控件的数据和frame, 子控件要添加到contentView中)
- 进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体\固定的图片)
3.提供2个模型
- 数据模型: 存放文字数据\图片数据
- frame模型: 存放数据模型\所有子控件的frame\cell的高度
4.重写frame模型里的数据模型的Setter方法:在这个方法中,设置子控制的frame和每个cell高度
5.cell拥有一个frame模型(不要直接拥有数据模型)
6.重写frame模型属性的setter方法: 在这个方法中设置子控件的显示数据和frame
7.frame模型数据的初始化已经采取懒加载的方式(每一个cell对应的frame模型数据只加载一次)
下面分步来写相应的代码
Cell视图设置
1.新建一个继承自UITableViewCell的类,重写initWithStyle:reuseIdentifier:方法
2.重写frame模型属性的setter方法: 在这个方法中设置子控件的显示数据和frame
StatusCell.h
#import <UIKit/UIKit.h> @class StatusFrame; @interface StatusCell : UITableViewCell @property (nonatomic, strong) StatusFrame *statusFrame; + (instancetype)cellWithTableView:(UITableView *)tableView; @end
StatusCell.m
// // StatusCell.m // 9.2 - 简单微博 // // Created by apple on 15/9/18. // Copyright (c) 2015年 weconex. All rights reserved. // // 昵称的字体 #define MJNameFont [UIFont systemFontOfSize:14] // 正文的字体 #define MJTextFont [UIFont systemFontOfSize:15] #import "StatusCell.h" #import "Status.h" #import "StatusFrame.h" @interface StatusCell() /** * 头像 */ @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 StatusCell /** * 重写initWithStyle:reuseIdentifier:方法 * 添加所有需要显示的子控件(不需要设置子控件的数据和frame,子控件要添加到contentView中) * 进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体\固定的图片) */ -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self==[super initWithStyle:style reuseIdentifier:reuseIdentifier]) { // 1.头像 UIImageView *iconView = [[UIImageView alloc] init]; [self.contentView addSubview:iconView]; self.iconView = iconView;//相当于xib的拉线 // 2.昵称 UILabel *nameView = [[UILabel alloc] init]; nameView.font = MJNameFont; [self.contentView addSubview:nameView]; self.nameView = nameView; // 3.会员图标 UIImageView *vipView = [[UIImageView alloc] init]; vipView.image = [UIImage imageNamed:@"vip"]; [self.contentView addSubview:vipView]; self.vipView = vipView; // 4.正文 UILabel *textView = [[UILabel alloc] init]; textView.numberOfLines = 0; textView.font = MJTextFont; [self.contentView addSubview:textView]; self.textView = textView; // 5.配图 UIImageView *pictureView = [[UIImageView alloc] init]; [self.contentView addSubview:pictureView]; self.pictureView = pictureView; } return self; } /** * 在这个方法中设置子控件的frame和显示数据 */ -(void)setStatusFrame:(StatusFrame *)statusFrame { _statusFrame = statusFrame; // 1.设置数据 [self settingData]; // 2.设置frame [self settingFrame]; } /** * 设置数据 */ - (void)settingData { // 微博数据 Status *status = self.statusFrame.status; // 1.头像 self.iconView.image = [UIImage imageNamed:status.icon]; // 2.昵称 self.nameView.text = status.name; // 3.会员图标 if (status.vip) { self.vipView.hidden = NO; self.nameView.textColor = [UIColor redColor]; } else { 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; } } /** * 设置frame */ - (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"; StatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[StatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } return cell; } @end
模型定义
提供2个模型
- 数据模型(Status): 存放文字数据\图片数据
- frame模型(StatusFrame): 存放数据模型\所有子控件的frame\cell的高度
重写frame模型里的数据模型(Status)的Setter方法:在这个方法中,设置子控制的frame和每个cell高度
Status.h
// // Status.h // 9.2 - 简单微博 // // Created by apple on 15/9/18. // Copyright (c) 2015年 weconex. All rights reserved. // #import <UIKit/UIKit.h> @interface Status : UICollectionViewCell @property (nonatomic, copy) NSString *text; // 内容 @property (nonatomic, copy) NSString *icon; // 头像 @property (nonatomic, copy) NSString *name; // 昵称 @property (nonatomic, copy) NSString *picture; // 配图 @property (nonatomic, assign) BOOL vip; - (instancetype)initWithDict:(NSDictionary *)dict; + (instancetype)statusWithDict:(NSDictionary *)dict; @end
Status.m
// // Status.m // 9.2 - 简单微博 // // Created by apple on 15/9/18. // Copyright (c) 2015年 weconex. All rights reserved. // #import "Status.h" @implementation Status - (instancetype)initWithDict:(NSDictionary *)dict { if (self = [super init]) { [self setValuesForKeysWithDictionary:dict]; } return self; } + (instancetype)statusWithDict:(NSDictionary *)dict { return [[self alloc] initWithDict:dict]; } @end
StatusFrame.h
// // StatusFrame.h // 9.2 - 简单微博 // // Created by apple on 15/9/18. // Copyright (c) 2015年 weconex. All rights reserved. // frame模型: 存放数据模型\所有子控件的frame\cell的高度 #import <UIKit/UIKit.h> @class Status; @interface StatusFrame : UITableViewCell /** * 头像的frame */ @property (nonatomic, assign, readonly) CGRect iconF; /** * 昵称的frame */ @property (nonatomic, assign, readonly) CGRect nameF; /** * 会员图标的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; /** * 存放数据模型 */ @property (nonatomic, strong) Status *status; @end
StatusFrame.m
// // StatusFrame.m // 9.2 - 简单微博 // // Created by apple on 15/9/18. // Copyright (c) 2015年 weconex. All rights reserved. // // 昵称的字体 #define MJNameFont [UIFont systemFontOfSize:14] // 正文的字体 #define MJTextFont [UIFont systemFontOfSize:15] #import "StatusFrame.h" #import "Status.h" @implementation StatusFrame /** * 计算文字尺寸 * * @param text 需要计算尺寸的文字 * @param font 文字的字体 * @param 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; } /** * 设置每一条微博的Frame 及cell的高度 * * @param status 微博模型 */ -(void)setStatus:(Status *)status { _status = status; // 子控件之间的间距 CGFloat padding = 10; // 1.头像 CGFloat iconX = padding; CGFloat iconY = padding; CGFloat iconW = 30; CGFloat iconH = 30; _iconF = CGRectMake(iconX, iconY, iconW, iconH); // 2.昵称 // 文字的字体 CGSize nameSize = [self sizeWithText:self.status.name font:MJNameFont maxSize:CGSizeMake(MAXFLOAT, MAXFLOAT)]; CGFloat nameX = CGRectGetMaxX(_iconF) + padding; CGFloat nameY = iconY + (iconH - nameSize.height) * 0.5; _nameF = CGRectMake(nameX, nameY, nameSize.width, nameSize.height); // 3.会员图标 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:self.status.text font:MJTextFont maxSize:CGSizeMake(300, MAXFLOAT)]; _textF = CGRectMake(textX, textY, textSize.width, textSize.height); // 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); _cellHeight = CGRectGetMaxY(_pictureF) + padding; } else { _cellHeight = CGRectGetMaxY(_textF) + padding; } } @end
控制器使用Cell
cell拥有一个frame模型(不要直接拥有数据模型)
frame模型数据的初始化已经采取懒加载的方式(每一个cell对应的frame模型数据只加载一次)
ViewController.m
// // ViewController.m // 9.2 - 简单微博 // // Created by apple on 15/9/18. // Copyright (c) 2015年 weconex. All rights reserved. // #import "ViewController.h" #import "StatusFrame.h" #import "Status.h" #import "StatusCell.h" @interface ViewController () /** * 存放所有cell的frame模型数据 */ @property (nonatomic, strong) NSArray *statusFrames; @end @implementation ViewController -(NSArray *)statusFrames { if (_statusFrames==nil) { // 初始化 // 1.获得plist的全路径 NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil]; // 2.加载数组 NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; // 3.将dictArray里面的所有字典转成模型对象,放到新的数组中 NSMutableArray *statusFrameArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { // 3.1.创建Status模型对象 Status *status = [Status statusWithDict:dict]; // 3.2.创建StatusFrame模型对象 StatusFrame *statusFrame = [[StatusFrame alloc] init]; statusFrame.status = status; // 3.2.添加模型对象到数组中 [statusFrameArray addObject:statusFrame]; } // 4.赋值 _statusFrames = statusFrameArray; } return _statusFrames; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } // 隐藏导航栏 - (BOOL)prefersStatusBarHidden { return YES; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.statusFrames.count; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 1.创建cell StatusCell *cell = [StatusCell cellWithTableView:tableView]; // 2.在这个方法算好了cell的高度 cell.statusFrame = self.statusFrames[indexPath.row]; // 3.返回cell return cell; } /** * 返回每个单元格的高度 */ -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { StatusFrame *statusFrame=self.statusFrames[indexPath.row]; return statusFrame.cellHeight; } @end
请按照步骤来,当然,上面的代码有一些步骤是混合在一起了。
源码下载:点击下载