1、 xib自定义cell之团购案例
UITableViewCell提供了四种Cell样式,但是自带的样式往往不能满足我们的开发需求。当系统自带cell样式不能满足我们开发需求时,就应该自己手动创建cell。
手动创建cell的两种方式:
当cell的布局样式相同,只是数据不同我们就可以使用xib快速创建cell。
当布局样式不同的时候,我们就不能使用xib来创建cell,而是需要重写初始化cell的方法代码创建cell。
先了解下简单的xib快速创建cell案例,我们需要做一个团购商品展示页面,如下图所示:点击这里查看动态图
需求:顶部有图片轮播,中间是商品列表,底部有加载更多按钮并且点击后能加载新的cell。
分析:从截图可以看出每个cell的布局都是相同的,有商品图片、名称、价格、购买数量。所以我们可以使用xib来创建,头部轮播器以前以后做过了,这里就不重点解释。今日、明日、后日推荐按钮和图片轮播是一个view。底部加载更多也是一个单独View,不过他们都随着tableview一起滚动,所以他们都是在tableview中。
首先不考虑界面,先创建项目导入素材和plist文件,并封装模型对象。
需求:顶部有图片轮播,中间是商品列表,底部有加载更多按钮并且点击后能加载新的cell。
分析:从截图可以看出每个cell的布局都是相同的,有商品图片、名称、价格、购买数量。所以我们可以使用xib来创建,头部轮播器以前以后做过了,这里就不重点解释。今日、明日、后日推荐按钮和图片轮播是一个view。底部加载更多也是一个单独View,不过他们都随着tableview一起滚动,所以他们都是在tableview中。
首先不考虑界面,先创建项目导入素材和plist文件,并封装模型对象。
JFGoods.h
#import <Foundation/Foundation.h>
@interface JFGoods : NSObject
@property (copy, nonatomic) NSString *buyCount;
@property (copy, nonatomic) NSString *icon;
@property (copy, nonatomic) NSString *price;
@property (copy, nonatomic) NSString *title;
//快速创建模型对象的对象方法
- (instancetype)initWithDictionary:(NSDictionary *)dict;
//快速创建模型对象的类方法
+ (instancetype)goodsWithDictionary:(NSDictionary *)dict;
//返回一个模型数组
+ (NSMutableArray *)goodss;
@end
JFGoods.m
#import "JFGoods.h"
@implementation JFGoods
//快速创建模型对象的对象方法
- (instancetype)initWithDictionary:(NSDictionary *)dict {
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
//快速创建模型对象的类方法
+ (instancetype)goodsWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
//返回一个模型数组
+ (NSMutableArray *)goodss {
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"tgs.plist" ofType:nil]];
NSMutableArray *arrayM = [NSMutableArray array];
for (NSDictionary *dict in array) {
JFGoods *goods = [JFGoods goodsWithDictionary:dict];
[arrayM addObject:goods];
}
return arrayM;
}
@end
封装好模型后, 创建xib文件 。注意这里是 UITableViewCell 控件而不是UIView, 因为只有UITableViewCell才能设置cell重用标识符 。
创建好xib后就创建封装cell类,注意这个类是继承自UITableViewCell。然后再xib的Custom Class选项中制定Class为我们创建的类,再进行控件属性拖线。然后定义对应方法,加载xib并创建创建cell,再对其进行子控件赋值数据。
JFGoodsCell.h
#import <UIKit/UIKit.h>
#import "JFGoods.h"
@interface JFGoodsCell : UITableViewCell
@property (strong, nonatomic) JFGoods *goods;
//快速创建cell
+ (instancetype)goodsCell:(UITableView *)tableView;
@end
JFGoodsCell.m
#import "JFGoodsCell.h"
@interface JFGoodsCell ()
//商品图片View
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
//商品标题View
@property (weak, nonatomic) IBOutlet UILabel *titleView;
//商品价格View
@property (weak, nonatomic) IBOutlet UILabel *priceView;
//商品购买次数View
@property (weak, nonatomic) IBOutlet UILabel *buyCountView;
@end
@implementation JFGoodsCell
//快速创建cell
+ (instancetype)goodsCell:(UITableView *)tableView {
//唯一标识符
static NSString *ID = @"goods";
//从缓存中创建cell
JFGoodsCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
//如果缓存中没有就创建
if (cell == nil) {
//从xib创建cell,xib已经指定了唯一标识符为goods。所以这里实现了cell重用
cell = [[[NSBundle mainBundle] loadNibNamed:@"JFGoodsCell" owner:nil options:nil] lastObject];
}
//设置单元行高度
tableView.rowHeight = 80;
return cell;
}
//重写set方法为控件设置数据
- (void)setGoods:(JFGoods *)goods {
_goods = goods;
//为控件加载数据
self.iconView.image = [UIImage imageNamed:goods.icon];
self.titleView.text = goods.title;
self.priceView.text = [NSString stringWithFormat:@"¥ %@",goods.price];
self.buyCountView.text = [NSString stringWithFormat:@"%@ 人已购买",goods.buyCount];
}
@end
顶部的UIView
我使用了网上下载的类库快速创建,类库地址:
https://github.com/gsdios/SDCycleScrollView 。下载类库,并导入我们的工程,再使用xib来创建一个UIView,在UIView中再创建一个用于展示轮播的UIView并进行控件属性连线,然后将创建好的轮播器添加到这个UIView中。
提供一个快速创建顶部View的类方法,并实现。
JFHeaderView.h
#import <UIKit/UIKit.h>
@interface JFHeaderView : UIView
//快速创建头部View
+ (instancetype)headerView;
@end
JFHeaderView.m
#import "JFHeaderView.h"
#import "SDCycleScrollView.h"
@interface JFHeaderView ()
//展示轮播的UIView
@property (weak, nonatomic) IBOutlet UIView *imgView;
@end
@implementation JFHeaderView
//快速创建头部View
+ (instancetype)headerView {
return [[[NSBundle mainBundle] loadNibNamed:@"JFHeaderView" owner:self options:nil] lastObject];
}
//当加载xib到界面之后,会调用此方法进行xib文件的初始化操作
- (void)awakeFromNib {
//一定要调用父类的awakeFromNib方法
[super awakeFromNib];
//封装图片对象
NSArray *images = @[[UIImage imageNamed:@"ad_00"],
[UIImage imageNamed:@"ad_01"],
[UIImage imageNamed:@"ad_02"],
[UIImage imageNamed:@"ad_03"],
[UIImage imageNamed:@"ad_04"]
];
CGFloat width = self.imgView.bounds.size.width;
//创建不带标题的图片轮播器
SDCycleScrollView *cycleScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 0, width, 120) imagesGroup:images];
//轮播是否循环
cycleScrollView.infiniteLoop = YES;
//设置页码样式
cycleScrollView.pageControlStyle = SDCycleScrollViewPageContolStyleAnimated;
//设置轮播时间间隔,默认1.0秒
cycleScrollView.autoScrollTimeInterval = 2.0;
//清除缓存
[cycleScrollView clearCache];
//添加轮播View到父容器中
[self.imgView addSubview:cycleScrollView];
}
@end
效果如图所示:
底部也是一个单独的UIView,也可以使用xib来创建。默认能点击,说明有一个Button,点击后Button隐藏Button并显示一个菊花转的控件和Label。拖好控件然后再进行LoadingView和LoadingBtn的属性连线。
加载更多数据的思路分析:
点击按钮后不仅仅是能隐藏按钮、显示加载View,还能实现真正的加载数据功能。要实现这个功能,我们必须要用到当前的tableview和模型数组,并能修改模型数组。
如果将控制器作为方法参数传递过来也是可以实现的,但是这不符合MVC设计思想,所以我们将控制器设为代理来帮我们完成控制器才能完成的事情。
给按钮添加单击事件,调用代理方法。在点击了按钮后,会触发单击事件,调用self中的方法来完成数据添加功能。
JFFooterView.h
#import <UIKit/UIKit.h>
@class JFFooterView;
//定义一个代理协议
@protocol JFFooterViewDelegate <NSObject>
//必须实现的代理方法
@required
- (void)footerViewDidClicking:(JFFooterView *)footerView;
@end
@interface JFFooterView : UIView
//快速创建底部View
+ (instancetype)footerView;
//底部View的代理属性
@property (weak, nonatomic) id<JFFooterViewDelegate> delegate;
@end
JFFooterView.m
#import "JFFooterView.h"
@interface JFFooterView ()
@property (weak, nonatomic) IBOutlet UIView *loadingView;//加载View
@property (weak, nonatomic) IBOutlet UIButton *loadingBtn;//加载Button
//点击加载按钮事件
- (IBAction)loadingClick;
@end
@implementation JFFooterView
//快速创建底部View
+ (instancetype)footerView {
return [[[NSBundle mainBundle] loadNibNamed:@"JFFooterView" owner:self options:nil] lastObject];
}
//点击加载按钮事件
- (IBAction)loadingClick {
//点击后让按钮隐藏,加载view取消隐藏
self.loadingBtn.hidden = YES;
self.loadingView.hidden = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//让按钮隐藏取消隐藏,加载view隐藏
self.loadingBtn.hidden = NO;
self.loadingView.hidden = YES;
//加载数据,判断是否实现了代理方法
if ([self.delegate respondsToSelector:@selector(footerViewDidClicking:)]) {
//调用代理方法,将加载更多数据功能交给代理来完成
[self.delegate footerViewDidClicking:self];
} else {
NSLog(@"没有实现代理方法");
}
});
}
@end
完成了封装数据模型、封装Cell、创建顶部和底部UIView后,最后来控制器进行各种调用。先在Main.storyboard中拖拽一个满屏的UITableView控件并进行属性连线操作。最后再控制器中加载数据、创建cell并实现加载更多数据的代理方法。
ViewController.m
#import "ViewController.h"
#import "JFGoods.h"
#import "JFGoodsCell.h"
#import "JFHeaderView.h"
#import "JFFooterView.h"
@interface ViewController () <UITableViewDataSource,JFFooterViewDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) NSMutableArray *goodss;
@end
@implementation ViewController
//隐藏状态栏
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
//指定数据源对象
self.tableView.dataSource = self;
//设置头部view
JFHeaderView *headerView = [JFHeaderView headerView];
self.tableView.tableHeaderView = headerView;
//设置底部view
JFFooterView *footerView = [JFFooterView footerView];
self.tableView.tableFooterView = footerView;
//让当前控制器作为底部View的代理对象
footerView.delegate = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//懒加载模型数组
- (NSArray *)goodss {
if (_goodss == nil) {
_goodss = [JFGoods goodss];
}
return _goodss;
}
//每组多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.goodss.count;
}
//创建cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//取出商品模型
JFGoods *goods = self.goodss[indexPath.row];
//创建cell
JFGoodsCell *cell = [JFGoodsCell goodsCell:tableView];
//为cell赋值数据
cell.goods = goods;
return cell;
}
//当点击了底部View后触发的代理方法
- (void)footerViewDidClicking:(JFFooterView *)footerView {
//创建商品信息字典
NSDictionary *dict = @{@"title" : @"镀金翔", @"icon" : @"5ee372ff039073317a49af5442748071", @"price" : @"10", @"buyCount" : @"1009"};
//创建商品模型
JFGoods *newGoods = [JFGoods goodsWithDictionary:dict];
//添加到模型数组
[self.goodss addObject:newGoods];
//刷新所有数据
[self.tableView reloadData];
//刷新单行数据,实现添加商品动画效果
NSIndexPath *path = [NSIndexPath indexPathForRow:self.goodss.count - 1 inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationBottom];
//重新设置偏移值
self.tableView.contentOffset = CGPointMake(0, CGRectGetMaxY(footerView.frame) - self.tableView.frame.size.height);
}
@end
案例易错总结:
1.通过plist加载模型数据,Controller中懒加载数据,设置tableview的数据源和代理对象。
2.默认情况xib和视图类是不能进行连线操作的,需要在xib中指定自定义类。
3.要给xib中的控件赋值,就必须把模型对象传递给cell,可以设置方法或直接定义模型属性。
4.加载xib创建cell时要注意xib不能加后缀名,并且是返回数组最后一个元素。
5.整个案例以MVC模式来设计,设置MVC逻辑group分类管理。
6.底部View加载更多数据用代理来完成,不要把控制器传递给底部View类。
2、 代码自定义cell之微博案例
当每个cell的结构样式不同的时候,我们就不能使用xib来快速创建cell,而是通过代码创建控件来创建cell。我们通过一个微博案例来演示代码创建cell的方法,下图就是微博案例完成后的截图:点击这里查看动态图
需求:实现微博消息界面,每个人都有头像且尺寸一样,每个人都有名字字数不一定相同,有vip就显示皇冠没有就不显示,正文自动换行,有配图就显示配图没有就不显示。
分析:从上图可以看出,cell中应该有头像、昵称、vip、正文、配图控件。且每个控件之间有个固定间距,vip的frame根据昵称的x坐标来决定,正文的高度决定了配图的坐标,配图尺寸可以定死。由此可得知,昵称、正文需要计算宽、高,并且是根据昵称的字数、字体来计算。
导入素材和plist文件,并封装模型。注意这里字典中国键值对个数不是统一的,有些没有picture,但我们封装的时候按照最多键值对的字典来定义属性。因为属性都有默认值,如果是NSString则默认为nil,将nil赋值给控件并不会对程序造成影响。
JFWeibo.h
#import <Foundation/Foundation.h>
@interface JFWeibo : 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;
//快速创建模型对象的对象方法
- (instancetype)initWithDictionary:(NSDictionary *)dict;
//快速创建模型对象的类方法
+ (instancetype)weiboWithDictionary:(NSDictionary *)dict;
//返回模型数组
+ (NSArray *)weibos;
@end
JFWeibo.m
#import "JFWeibo.h"
@implementation JFWeibo
//快速创建模型对象的对象方法
- (instancetype)initWithDictionary:(NSDictionary *)dict {
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
//快速创建模型对象的类方法
+ (instancetype)weiboWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
//返回模型数组
+ (NSArray *)weibos {
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil]];
NSMutableArray *arrayM = [NSMutableArray array];
for (NSDictionary *dict in array) {
JFWeibo *weibo = [JFWeibo weiboWithDictionary:dict];
[arrayM addObject:weibo];
}
return arrayM;
}
@end
代码自定义cell的高度每行都不同,所以一般不能通过tableView.rowHeight属性来定义。我们需要根据每个cell子控件的frame计算每个cell的高度,并且需要通过UITableView的代理方法来指定每个cell的高度。
但是:指定cell高度的代理方法是在创建调用创建cell的方法之前调用的,也就是我们需要在创建cell之前就计算出cell子控件的frame和cell的高度。
这个UITableView的代理方法返回每个cell的高度,执行优先级高于下面返回cell的方法。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
这个UITableView的数据源方法返回cell,但是他的调用优先级比上面的代理方法优先级低,也就是后执行这个方法。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
通过上面的分析得之,我们需要在创建cell之前计算好cell子控件的frame和cell的高度。所以我们封装一个cell的frame模型类,通过模型数据来计算cell子控件的frame和cell的高度。
JFWeiboFrame.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class JFWeibo;
//在这里定于字体宏,当其他类导入头文件后也能使用
#define nameFont [UIFont systemFontOfSize:18]
#define textFont [UIFont systemFontOfSize:16]
@interface JFWeiboFrame : NSObject
//数据模型对象
@property (strong, nonatomic) JFWeibo *weibo;
//cell高度
@property (assign, nonatomic, readonly) CGFloat cellHeight;
//各个子控件的frame
@property (assign, nonatomic, readonly) CGRect iconFrame;
@property (assign, nonatomic, readonly) CGRect nameFrame;
@property (assign, nonatomic, readonly) CGRect vipFrame;
@property (assign, nonatomic, readonly) CGRect textFrame;
@property (assign, nonatomic, readonly) CGRect pictureFrame;
//返回frame模型数组
+ (NSArray *)weiboFrames;
@end
JFWeiboFram.m
#import "JFWeiboFrame.h"
#import "JFWeibo.h"
//每个子控件之间的间距
#define margin 10
@implementation JFWeiboFrame
//返回frame模型数组
+ (NSArray *)weiboFrames {
NSArray *array = [JFWeibo weibos];
NSMutableArray *arrayM = [NSMutableArray array];
for (JFWeibo *weibo in array) {
//创建frame模型
JFWeiboFrame *weiboFrame = [[JFWeiboFrame alloc] init];
//为frame模型赋值模型数据,因为重写了set方法。所以会在赋值后计算cell子控件的frame和行高
weiboFrame.weibo = weibo;
//将计算好cell子控件的frame和行高的frame模型存到数组
[arrayM addObject:weiboFrame];
}
return arrayM;
}
//重写set方法,初始化frame和行高
- (void)setWeibo:(JFWeibo *)weibo {
_weibo = weibo;
//初始化frame
[self loadingFrame];
//初始化行高
[self loadingRowHeight];
}
//初始化frame
- (void)loadingFrame {
//头像
CGFloat iconW = 30;
CGFloat iconH = iconW;
CGFloat iconX = margin;
CGFloat iconY = iconX;
_iconFrame = CGRectMake(iconX, iconY, iconW, iconH);
//名字
CGSize nameSize = [self.weibo.name boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : nameFont} context:nil].size;
CGFloat nameW = nameSize.width;
CGFloat nameH = nameSize.height;
CGFloat nameX = CGRectGetMaxX(_iconFrame) + margin;
CGFloat nameY = (iconH - nameH) / 2 + margin;
_nameFrame = CGRectMake(nameX, nameY, nameW, nameH);
//vip
CGFloat vipW = 14;
CGFloat vipH = 14;
CGFloat vipX = CGRectGetMaxX(_nameFrame) + margin;
CGFloat vipY = (iconH - vipH) / 2 + margin;
_vipFrame = CGRectMake(vipX, vipY, vipW, vipH);
//正文
CGSize textSize = [self.weibo.text boundingRectWithSize:CGSizeMake(355, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : textFont} context:nil].size;
CGFloat textW = textSize.width;
CGFloat textH = textSize.height;
CGFloat textX = margin;
CGFloat textY = CGRectGetMaxY(_iconFrame) + margin;
_textFrame = CGRectMake(textX, textY, textW, textH);
//配图
CGFloat pictureW = 100;
CGFloat pictureH = 100;
CGFloat pictureX = margin;
CGFloat pictureY = CGRectGetMaxY(_textFrame) + margin;
_pictureFrame = CGRectMake(pictureX, pictureY, pictureW, pictureH);
}
//初始化行高
- (void)loadingRowHeight {
if (self.weibo.picture) {
_cellHeight = CGRectGetMaxY(_pictureFrame) + margin;
} else {
_cellHeight = CGRectGetMaxY(_textFrame) + margin;
}
}
@end
封装好cell的frame后就开始封装cell,因为我们每个cell的布局样式不同,所以只能使用代码创建子控件自定义cell。封装cell的类中不需要模型属性,直接定于frame模型属性就行了,因为frame模型属性中就有模型属性。
JFWeiboFrame.h
#import <UIKit/UIKit.h>
@class JFWeiboFrame;
@interface JFWeiboCell : UITableViewCell
//frame模型对象属性
@property (strong, nonatomic) JFWeiboFrame *weiboFrame;
//快速创建cell
+ (instancetype)weiboCell:(UITableView *)tableView;
@end
注意我们需要重写initWithStyle:reuseIdentifier:方法创建cell的子控件,因为系统自带的cell样式和xib都不能使用,只能代码创建cell的子控件来展示数据。
JFWeiboFrame.m
#import "JFWeiboCell.h"
#import "JFWeibo.h"
#import "JFWeiboFrame.h"
@interface JFWeiboCell ()
@property (weak, nonatomic) UIImageView *iconView; //头像
@property (weak, nonatomic) UILabel *nameView; //名字
@property (weak, nonatomic) UIImageView *vipView; //vip
@property (weak, nonatomic) UILabel *textView; //文本
@property (weak, nonatomic) UIImageView *pictureView; //配图
@end
@implementation JFWeiboCell
//快速创建cell
+ (instancetype)weiboCell:(UITableView *)tableView {
static NSString *ID = @"weibo";
//从缓存中创建cell
JFWeiboCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
//缓存中么有cell就新创建,这里的默认初始化方法不能满足需求,需重写
cell = [[JFWeiboCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
return cell;
}
//重写initWithStyle:reuseIdentifier:方法创建cell的子控件
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
//重写父类初始化方法,需先调用父类初始化方法初始化父类成员
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
//头像
UIImageView *iconView = [[UIImageView alloc] init];
[self addSubview:iconView];
self.iconView = iconView;
//名字
UILabel *nameView = [[UILabel alloc] init];
nameView.font = nameFont; //设置字体
[self addSubview:nameView];
self.nameView = nameView;
//vip
UIImageView *vipView = [[UIImageView alloc] init];
[self addSubview:vipView];
self.vipView = vipView;
//正文
UILabel *textView = [[UILabel alloc] init];
textView.font = textFont; //设置字体
textView.numberOfLines = 0; //自动换行
[self addSubview:textView];
self.textView = textView;
//配图
UIImageView *pictureView = [[UIImageView alloc] init];
[self addSubview:pictureView];
self.pictureView = pictureView;
}
return self;
}
//重写set方法为cell的子控件赋值
- (void)setWeiboFrame:(JFWeiboFrame *)weiboFrame {
_weiboFrame = weiboFrame;
//加载子控件数据
[self loadingData];
//设置子控件frame
[self setWeiboFrame];
}
- (void)loadingData {
//取出数据模型
JFWeibo *weibo = self.weiboFrame.weibo;
//头像
self.iconView.image = [UIImage imageNamed:weibo.icon];
//名字
self.nameView.text = weibo.name;
//vip
if (weibo.isVip) {
//如果是vip就取消隐藏vip图标
self.vipView.hidden = NO;
self.vipView.image = [UIImage imageNamed:@"vip"];
} else {
//不是vip就隐藏vip图标
self.vipView.hidden = YES;
}
//正文
self.textView.text = weibo.text;
//配图
self.pictureView.image = [UIImage imageNamed:weibo.picture];
}
- (void)setWeiboFrame {
//头像
self.iconView.frame = self.weiboFrame.iconFrame;
//名字
self.nameView.frame = self.weiboFrame.nameFrame;
//vip
self.vipView.frame = self.weiboFrame.vipFrame;
//正文
self.textView.frame = self.weiboFrame.textFrame;
//配图
self.pictureView.frame = self.weiboFrame.pictureFrame;
}
@end
封装好cell后,我们就可以开始在控制台调用了。首先先在
Main.storyboard
中删除原有的控制器,拖拽一个新的控制器控件
UITableViewController
。这样做的好处是
我们不需要手动添加UITableView数据源、代理协议,手动指定数据源、代理对象,会默认帮我们设置好这一切
。
特别注意的是:必须勾选Is initial View Controller选项,让当前控制器设为默认视图控制器,否则看不到界面。
最后在控制器中定义一个frame模型数组属性并懒加载数据,然后实现数据源方法和代理方法,创建cell并为cell赋值数据。
ViewController.m
#import "ViewController.h"
#import "JFWeiboFrame.h"
#import "JFWeiboCell.h"
@interface ViewController ()
//frame模型数组
@property (strong, nonatomic) NSArray *weiboFrames;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
//隐藏状态栏
- (BOOL)prefersStatusBarHidden {
return YES;
}
//懒加载frame模型数组
- (NSArray *)weiboFrames {
if (_weiboFrames == nil) {
//返回一个创建好的frame模型数组
_weiboFrames = [JFWeiboFrame weiboFrames];
}
return _weiboFrames;
}
//每组多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//模型数组有多少个元素就有多少行
return self.weiboFrames.count;
}
//创建cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//获取模型对象
JFWeiboFrame *weiboFrame = self.weiboFrames[indexPath.row];
//创建cell
JFWeiboCell *cell = [JFWeiboCell weiboCell:tableView];
//为cell赋值
cell.weiboFrame = weiboFrame;
return cell;
}
//返回cell行高
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//获取模型对象
JFWeiboFrame *weiboFrame = self.weiboFrames[indexPath.row];
return weiboFrame.cellHeight;
}
@end
案例易错总结:
1.创建cell的子控件是在重写initWithStyle:reuseIdentifier:方法中创建。
2.封装frame模型的意义就是为了计算cell的高度,因为设置高度的代理方法优先级高于创建cell的数据源方法。
3.使用UITableViewController控制器,可以节省我们遵循协议、指定代理的步骤。