031-UITableView(2)-iOS笔记

/ 0

学习目标

1.【掌握】xib自定义cell之团购案例

2.【掌握】代码自定义cell之微博案例

一、xib自定义cell之团购案例

UITableViewCell提供了四种Cell样式,但是自带的样式往往不能满足我们的开发需求。当系统自带cell样式不能满足我们开发需求时,就应该自己手动创建cell。

手动创建cell的两种方式:

当cell的布局样式相同,只是数据不同我们就可以使用xib快速创建cell。

当布局样式不同的时候,我们就不能使用xib来创建cell,而是需要重写初始化cell的方法代码创建cell。

先了解下简单的xib快速创建cell案例,我们需要做一个团购商品展示页面,如下图所示:点击这里查看动态图

QQ20150816-1

需求:顶部有图片轮播,中间是商品列表,底部有加载更多按钮并且点击后能加载新的cell。

分析:从截图可以看出每个cell的布局都是相同的,有商品图片、名称、价格、购买数量。所以我们可以使用xib来创建,头部轮播器以前以后做过了,这里就不重点解释。今日、明日、后日推荐按钮和图片轮播是一个view。底部加载更多也是一个单独View,不过他们都随着tableview一起滚动,所以他们都是在tableview中。

首先不考虑界面,先创建项目导入素材和plist文件,并封装模型对象。

Snip20150816_2

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重用标识符

Snip20150816_4

创建好xib后就创建封装cell类,注意这个类是继承自UITableViewCell。然后再xib的Custom Class选项中制定Class为我们创建的类,再进行控件属性拖线。然后定义对应方法,加载xib并创建创建cell,再对其进行子控件赋值数据。

Snip20150816_5

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中。

Snip20150816_8

提供一个快速创建顶部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

效果如图所示:

top

底部也是一个单独的UIView,也可以使用xib来创建。默认能点击,说明有一个Button,点击后Button隐藏Button并显示一个菊花转的控件和Label。拖好控件然后再进行LoadingView和LoadingBtn的属性连线。

Snip20150816_9

加载更多数据的思路分析

点击按钮后不仅仅是能隐藏按钮、显示加载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类。

二、代码自定义cell之微博案例

当每个cell的结构样式不同的时候,我们就不能使用xib来快速创建cell,而是通过代码创建控件来创建cell。我们通过一个微博案例来演示代码创建cell的方法,下图就是微博案例完成后的截图:点击这里查看动态图

Snip20150817_15

需求:实现微博消息界面,每个人都有头像且尺寸一样,每个人都有名字字数不一定相同,有vip就显示皇冠没有就不显示,正文自动换行,有配图就显示配图没有就不显示。

分析:从上图可以看出,cell中应该有头像、昵称、vip、正文、配图控件。且每个控件之间有个固定间距,vip的frame根据昵称的x坐标来决定,正文的高度决定了配图的坐标,配图尺寸可以定死。由此可得知,昵称、正文需要计算宽、高,并且是根据昵称的字数、字体来计算。

导入素材和plist文件,并封装模型。注意这里字典中国键值对个数不是统一的,有些没有picture,但我们封装的时候按照最多键值对的字典来定义属性。因为属性都有默认值,如果是NSString则默认为nil,将nil赋值给控件并不会对程序造成影响。

Snip20150817_17

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数据源、代理协议,手动指定数据源、代理对象,会默认帮我们设置好这一切

Snip20150817_21特别注意的是:必须勾选Is initial View Controller选项,让当前控制器设为默认视图控制器,否则看不到界面。

Snip20150817_22

最后在控制器中定义一个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控制器,可以节省我们遵循协议、指定代理的步骤。