TableView(一)

TableView使用

1.tableView如何显示数据

    - 设置dataSource数据源方法
    - 数据源要遵守UITableViewDataSource协议
    - 数据源要实现协议中的某些方法
1.告诉tableView第Section组有多少行
-(NSInterger)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath

2.告诉tableView第indexPath行显示怎样的cell
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexpath

3.告诉TableView第section组头部标题
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section

4.告诉tableView第section组的尾部标题
-(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section

UITableView的常见设置

//1.分割线颜色
self.tableView.separetorColor = [UIColor redColor];
//2.隐藏分割线
self.tableView.separatorStyle = [UITableViewCellSeparatorStyleNone];
//3.取消选中样式
cell.selectionStyle = UITableViewCellSelectionStyleNone;
//4.设置选中的背景色
UIView *selectedBackgroundView = [[UIView alloc] init];
selectedBackgroundView.backgroundColor = [UIColor redColor];
cell.selectedBackgroundView = selectedBackgroundView;
//5.设置默认的背景色
cell.backgroundColor = [UIColor blueColor];
//6.设置默认的背景色
UIView *backgroundView = [[UIView alloc] init];
backgroundView.backgroundColor = [UIcolor greenColor];
cell.backgroundView = backgroundView;
//7.设置指示器
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.accessoryView = [[UISwitch alloc] init];

cell的循环利用

1.什么时候调用cell呢?

每当有一个cell进入视野范围内就会调用


1.循环利用方式1

/**
 *  什么时候调用:每当有一个cell进入视野范围内就会调用
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 0.重用标识
    // 被static修饰的局部变量:只会初始化一次,在整个程序运行过程中,只有一份内存
    static NSString *ID = @"cell";

    // 1.先根据cell的标识去缓存池中查找可循环利用的cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    // 2.如果cell为nil(缓存池找不到对应的cell)
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
    }

    // 3.覆盖数据
    cell.textLabel.text = [NSString stringWithFormat:@"testdata - %zd", indexPath.row];

    return cell;
}

2.循环利用方式2
(1)定义一个全局变量
//定义重用标识

NSString *ID = @"cell";

(2)注册某个标识对应的cell类型
//在viewDidLoad中注册cell

- (void)viewDidLoad {
    [super viewDidLoad];

    // 注册某个标识对应的cell类型
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ID];
}

(3)在数据源方法中返回cell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //1.去缓存池中查找cell
    UITabelViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    //2.覆盖数据
    cell.textLabel.text = [NSString stringWithFormat:@"testdata - %zd",indexPath.row];
    return cell;

自定义cell

xib自定义等高cell

  • 1.创建一个继承自UITableViewCell的子类,比如DealCell
  • 2.创建一个xib文件(文件名建议和cell的类名一样,比如DealCell.xib)
    - 拖拽一个UITableViewCell出来
    - 修改cell的class为DealCell
    - 设置cell的重用标识
    - 往cell中添加需要用到的子控件
  • 3.在控制器中
    • 利用registerNib....方法中注册xib文件
    • 利用重用标识找到cell(如果没有注册xib文件,就需要手动去加载xib文件)
    • 给cell传递模型数据
  • 4.在DealCell中
    • 将xib中的子控件连线到类拓展中 即DealCell.m文件中
    • 需要提供一个模型属性,重写模型的set方法,在这个方法中设置模型数据到子控件上
    • 也可以将创建获得cell的代码封装起来

重写set方法 设置数据

- (void)setDeal:(XMGDeal *)deal
{
    _deal = deal;
    
    // 设置数据
    self.iconView.image = [UIImage imageNamed:deal.icon];
    self.titleLabel.text = deal.title;
    self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price];
    self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已购买", deal.buyCount];
}



模型比如如下代码:

Status.h


@interface XMGStatus : NSObject

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *text;
@property (strong, nonatomic) NSString *icon;
@property (strong, nonatomic) NSString *picture;
@property (assign, nonatomic, getter=isVip) BOOL vip;
+ (instancetype)statusWithDict:(NSDictionary *)dict;

@end

Status.m


@implementation XMGStatus
+ (instancetype)statusWithDict:(NSDictionary *)dict
{
    XMGStatus *status = [[self alloc] init];
    [status setValuesForKeysWithDictionary:dict];
    return status;
}
@end

代码自定义cell(frame)

  • 1.创建一个继承自UITableViewCell的子类,比如DealCell
    • 在initWithStyle:reuseIdentifier:方法中
    • 添加子控件
    • 设置子控件的初始化属性(比如文字颜色、字体)
  • 2.在控制器中
    • 利用registerClass...方法注册DealCell类
    • 利用重用标识找到cell(如果没有注册类,就需要手动创建cell)
    • 给cell传递模型数据
    • 也可以将创建获得的cell的代码封装起来

在initWithStyle:reuseIdentifier方法中添加子控件

// 1.在initWithStyle:reuseIdentifier:方法中添加子控件

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        UIImageView *iconView = [[UIImageView alloc] init];
        [self.contentView addSubview:iconView];
        self.iconView = iconView;
        
        UILabel *titleLabel = [[UILabel alloc] init];
        [self.contentView addSubview:titleLabel];
        self.titleLabel = titleLabel;
        
        UILabel *priceLabel = [[UILabel alloc] init];
        priceLabel.textColor = [UIColor orangeColor];
        [self.contentView addSubview:priceLabel];
        self.priceLabel = priceLabel;
        
        UILabel *buyCountLabel = [[UILabel alloc] init];
        buyCountLabel.textAlignment = NSTextAlignmentRight;
        buyCountLabel.font = [UIFont systemFontOfSize:14];
        buyCountLabel.textColor = [UIColor lightGrayColor];
        [self.contentView addSubview:buyCountLabel];
        self.buyCountLabel = buyCountLabel;
    }
    return self;
}

fram代码如下:

// 2.在layoutSubviews方法中设置子控件的frame
- (void)layoutSubviews
{
    [super layoutSubviews];
    
    CGFloat contentH = self.contentView.frame.size.height;
    CGFloat contentW = self.contentView.frame.size.width;
    CGFloat margin = 10;
    
    CGFloat iconX = margin;
    CGFloat iconY = margin;
    CGFloat iconW = 100;
    CGFloat iconH = contentH - 2 * iconY;
    self.iconView.frame = CGRectMake(iconX, iconY, iconW, iconH);
    
    // titleLabel
    CGFloat titleX = CGRectGetMaxX(self.iconView.frame) + margin;
    CGFloat titleY = iconY;
    CGFloat titleW = contentW - titleX - margin;
    CGFloat titleH = 21;
    self.titleLabel.frame = CGRectMake(titleX, titleY, titleW, titleH);
//    CGRectMake(titleX, titleY�, titleW, titleH);
    
    // priceLabel
    CGFloat priceX = titleX;
    CGFloat priceH = 21;
    CGFloat priceY = contentH - margin - priceH;
    CGFloat priceW = 70;
    self.priceLabel.frame = CGRectMake(priceX, priceY, priceW, priceH);
    
    // buyCountLabel
    CGFloat buyCountH = priceH;
    CGFloat buyCountY = priceY;
    CGFloat buyCountX = CGRectGetMaxX(self.priceLabel.frame) + margin;
    CGFloat buyCountW = contentW - buyCountX - margin;
    self.buyCountLabel.frame = CGRectMake(buyCountX, buyCountY, buyCountW, buyCountH);
}

代码自定义cell(autolayout)

  • 1.创建一个继承自UITableViewCell的子类,比如XMGDealCell
    • 在initWithStyle:reuseIdentifier:方法中
    • 添加子控件
    • 添加子控件的约束(建议使用Masonry
    • 设置子控件的初始化属性(比如文字颜色、字体)
    • 需要提供一个模型属性,重写模型的set方法,在这个方法中设置模型数据到子控件
  • 2.在控制器中
    • 利用registerClass...方法注册XMGDealCell类
    • 利用重用标识找到cell(如果没有注册类,就需要手动创建cell)
    • 给cell传递模型数据
    • 也可以将创建获得cell的代码封装起来(比如cellWithTableView:方法)

autolayout代码如下


- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(test1) withObject:self afterDelay:5];//5秒后执行test1
    
    //蓝色控件
    UIView *blueView = [[UIView alloc] init];
    blueView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:blueView];
    
    //红色控件
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    
    //添加约束
    CGFloat margin = 20;
    CGFloat height = 50;
    //蓝色控件的约束
    [blueView makeConstraints:^(MASConstraintMaker *make){
        make.left.equalTo(self.view.left).offset(margin);//左边与父控件间距20
        make.right.equalTo(redView.left).offset(-margin);//右边与红色控件间距20
        make.bottom.equalTo(self.view.bottom).offset(-margin);//底部与父控件的底部间距20
        make.height.equalTo(height);//
        make.top.equalTo(redView.top);
        make.bottom.equalTo(redView.bottom);
        make.width.equalTo(redView.width);
    }];
    
    //红色控件的约束
    [redView makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(self.view.right).offset(-margin);
    }];
  
}

!!非等高的cell

xib自定义(重点)

  • 1.创建一个继承自UITableViewCell的子类,比如XMGDealCell
  • 2.创建一个xib文件(文件名建议跟cell的类名一样),比如StatusCell.xib
    • 拖拽一个UITableViewCell出来
    • 修改cell的class为StatusCell
    • 设置cell的重用标识
    • 往cell中添加需要用到的子控件
  • 3.在控制器中
    • 利用registerNib...方法注册xib文件
    • 利用重用标识找到cell(如果没有注册xib文件,就需要手动去加载xib文件)
    • 给cell传递模型数据
  • 4.在XMGDealCell中
    - 将xib中的子控件连线到类扩展中
    - 需要提供一个模型属性,重写模型的set方法,在这个方法中设置模型数据到子控件上
    - 也可以将创建获得cell的代码封装起来(比如cellWithTableView:方法)
  • 在模型中增加一个cellHeight属性,用来存放对应cell的高度
  • 在cell的模型属性set方法中调用[self layoutIfNeed]方法强制布局,然后计算出模型的cellheight属性值
  • 在控制器中实现tableView:estimatedHeightForRowAtIndexPath:方法,返回一个估计高度,比如200
  • 在控制器中实现tableView:heightForRowAtIndexPath:方法,返回cell的真实高度(模型中的cellHeight属性)

示例模型代码如下

status.h

//微博模型
@interface Status_M : NSObject
@property (nonatomic,strong) NSSet *name;
@property (nonatomic,strong) NSSet *text;
@property (nonatomic,strong) NSString *icon;
@property (nonatomic,strong) NSString *picture;
@property (assign, nonatomic, getter=isVip) BOOL vip;

//cell的高度
@property (assign, nonatomic) CGFloat cellHeight;
+(instancetype)statusWithDict:(NSDictionary *)dict

status.m

@implementation Status_M
+(instancetype)statusWithDict:(NSDictionary *)dict
{
    Status_M *status = [[self alloc] init];
    [status setValuesForKeysWithDictionary:dict];
    return status;
}
@end

自定义cell封装代码如下

StatusCell.h

#import <UIKit/UIKit.h>
@class Status_M;
@interface StatusCell : UITableViewCell
+(instancetype)cellWithTableView:(UITableView *)tableView;

//微博模型数据
@property (nonatomic, strong) Status_M *status;
@end

StatusCell.m

#import "StatusCell.h"
#import "Status_M.h"

@interface StatusCell()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UIImageView *vipView;
@property (weak, nonatomic) IBOutlet UILabel *contentLabel;
@property (weak, nonatomic) IBOutlet UIImageView *pictureView;
@end


@implementation StatusCell
+(instancetype)cellWithTableView:(UITableView *)tableView
{
    static NSString *ID = @"status";
    StatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
    if (cell == nil) {
        cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
    }
    return cell;
}


//设置label每行文字的最大宽度:为了保证计算出来的数值跟 真正显示出来的 效果一致
-(void)awakeFromNib
{
    self.contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
}


-(void)setStatus:(Status_M *)status
{
    _status = status;
    //判断是否是会员
    if (status.isVip) {
        self.nameLabel.textColor = [UIColor orangeColor];
        self.vipView.hidden = NO;
        
    }else{
        self.nameLabel.textColor = [UIColor blackColor];
        self.vipView.hidden = YES;
        
    }
    self.nameLabel.text = status.name;
    self.iconView.image = [UIImage imageNamed:status.icon];
    if (status.picture) {
        self.pictureView.hidden = NO;
        self.pictureView.image = [UIImage imageNamed:status.picture];
    }else{
        self.pictureView.hidden = YES;
    }
    self.contentLabel.text = status.text;
    
    //强制布局
    [self layoutIfNeeded];
    
    //计算cell的高度
    if (self.pictureView.hidden) {//没有配图
        
        status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10;
        
    }else{//配图
        status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10;
    }
}
@end

设置cellheight

@interface StatusesViewController()
@property (strong, nonatomic) NSArray *statuses;

@end

@implementation StatusesViewController

-(NSArray *)statuses
{
    if (_statuses == nil) {
        //加载plist中的字典数组
        NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil];
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
        
        //字典数组-->模型数组
        NSMutableArray *statusArray = [NSMutableArray array];
        for (NSDictionary *dict in dictArray) {
            Status_M *status = [Status_M statusWithDict: dict];
            [statusArray addObject:status];
        }
        _statuses = statusArray;
        
    }
    return  _statuses;
}


-(void)viewDidLoad
{
    [super viewDidLoad];
}

#pragma mark data source
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return  self.statuses.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    StatusCell *cell = [StatusCell cellWithTableView:tableView];
    cell.status = self.statuses[indexPath.row];
    return cell;
}

/**
 *  返回每一行的高度
 */

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    Status_M *status = self.statuses[indexPath.row];
    return status.cellHeight;
}

/**
 *  返回每一行的估计高度
    只要返回了估计高度,那么就会先条用tableView:cellForRowAtIndexPath:方法创建cell,再调用tableView heightForRowAtIndexPath:方法获取测cell的真实高度

 *
 */
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
    return 200;
}
@end

推荐阅读更多精彩内容