第四章 UITableView

有两种形式

  • UITableViewStylePlain:是正常的表格形式
  • UITableViewStyleGrouped:分组形式

如何展示数据

  • UITableView需要一个数据源(dataSource)来显示数据
  • UITableView会向数据源查询一共有多少行数据以及每一行显示什么数据等
  • 凡是遵守UITableViewDataSource代理都可以成为其数据源

  • 循环利用,这里面并没有循环利用


/**
 * 告诉tableView有多少组
 */
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
/**
 *  告诉tableView每组有多少行
 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section


/**
 *  告诉tableView的每行显示什么样的cell
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell * cell = [[UITableViewCell alloc] init];
    JXCarGroup * group = self.groups[indexPath.section];
    JXCar * car = group.cars[indexPath.row];
    cell.textLabel.text = car.name;
    cell.imageView.image = [UIImage imageNamed:car.icon];
    return cell;
}


/**
 *  告诉tableView的 头部标题
 */
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section


/**
 *  诉tableView的尾部标题
 */
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
  • 循环利用多个cell
/**
 *  复用不同的cell
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    if (indexPath.row % 3 == 0) {
        static NSString * identifier = @"cell";
        UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
        cell.textLabel.text = [NSString stringWithFormat:@"测试数据cell  ---    %zd",indexPath.row];
        return cell;
    } else if (indexPath.row % 3 == 1) {
        static NSString * identifier = @"cell";
        UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];

        cell.textLabel.text = [NSString stringWithFormat:@"测试数据cell1  ---    %zd",indexPath.row];
        return cell;
    } else {
        static NSString * identifier = @"cell";
        UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];

        cell.textLabel.text = [NSString stringWithFormat:@"测试数据cell2  ---    %zd",indexPath.row];
        return cell;
    }
}
  • 优化UITableViewCell

  • 优化方法一

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    // 0.重用标识
    // 被static修饰的局部变量:只会初始化一次,在整个程序中只有一份内存
    static NSString * ID = @"cell";
    // 1.先去缓存池中寻找可循环利用的cell
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) { // 如果cell为空,缓存池中没有可复用的cell
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];

    }
    cell.textLabel.text = [NSString stringWithFormat:@"测试数据 -- %zd",indexPath.row];
    return cell;
}
  • 优化方法二:提前注册

    - (void)viewDidLoad {
    [super viewDidLoad];
    
    // 注册cell
    // 这么做的好处就是 在dataSource方法中复用cell的时候,当最开始cell为空的时候直接返回 UITableViewCell
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
    }
    // 这样子就可以直接使用了
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString * identifier = @"cell";
    
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    JXHero * hero = self.heroes[indexPath.row];
    cell.textLabel.text = hero.name;
    cell.imageView.image = [UIImage imageNamed:hero.icon];
    cell.detailTextLabel.text = hero.intro;
    return cell;
    }
    

UITableViewController

  • 在UITableViewController中
// 在UITableViewController控制器中
    self.view;
    self.tableView;所表达的是一个东西
  • 在storyboard中设置UITableView的Dynamic Prototypes 的cell,可以设置不同的缓存池中的cell重用标识
  • 在代码中利用重标识获取不同的cell

不仅仅是在UITableViewController中才会有cell表格,在ViewController中拖一个tableView,设置Dynamic Prototypes为大于0的数字也回有cell表格

  • 优化方法三:直接在storyboard中设置

    • 在storyboard中设置UITableView的Dynamic Prototypes 的cell,可以设置不同的缓存池中的cell重用标识
    • 在代码中利用重标识获取不同的cell

      // 这样子就可以直接使用了
      - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      // 这里的identifier,设置需要跟storyboard中设置的是一样的。就不需要再次进行注册,可以直接使用
      static NSString * identifier = @"cell";
      
      UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
      JXHero * hero = self.heroes[indexPath.row];
      cell.textLabel.text = hero.name;
      cell.imageView.image = [UIImage imageNamed:hero.icon];
      cell.detailTextLabel.text = hero.intro;
      return cell;
      }
      
  • UITableViewController 设置

    //分割线颜色
    self.tableView.separatorColor = [UIColor redColor];
    // 分割线样式
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
  • cell设置,cell选中样式
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
  • cell设置,选中颜色
UIView * selectView = [[UIView alloc] init];
    selectView.backgroundColor = [UIColor orangeColor];
    cell.selectedBackgroundView = selectView;
  • cell设置,背景颜色
// 设置背景颜色
    cell.backgroundColor = [UIColor redColor];
  • cell设置指示器

    // 设置指示器
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;

    // 直接设置图片
    cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icc"]];

自定义cell

  • 等高的cell
    • storyboard定义cell
      • 1.创建一个继承自的UItableViewCell的子类
      • 2.在storyboard中
        • 在cell中添加子控件
        • 设置cell的重用标识
        • 重新设置cell的Class
      • 3.在控制器中
        • 利用重用标识找到cell
        • 给cell传递模型数据
      • 4.在自定义的cell中
        • 将storyboard中的子控件连线到类扩展中去
        • 提供一个模型属性,重写模型属性的set方法,在这个方法中把模型属性设置到子控件上

源代码:用storyboard创建等高cell

  • xib定义cell

    • 1.先创建一个UITableViewCell子类
    • 2.创建一个跟cell同名的xib
      • 拖拽一个UITableViewCell出来
      • 修改cell的Class为我们设置的类
      • 设置cell的重用标识
      • 往cell中添加控件
    • 3.在控制器中

      • 利用registeNib...注册XIB文件
      • 如果没有注册xib文件,就需要手动加载xib文件 (NSBundle mainBundle)
      • 给cell传递模型数据
    • 4.在自定义cell中

      • 将xib中控件连线到类扩展中
      • 提供一个模型属性,重写模型属性的set方法,在这个方法中把模型属性设置到子控件上
      • 或者将获得cell的xib文件封装起来

源码地址:用xib封装cell方法

  • 代码定义cell
  • 非等高的cell

    • xib自定义cell
      • 1.先创建一个UITableViewCell子类,勾选xib
      • 2.设置cell重用标识,添加控件,设置约束
      • 3.在cell中封装重用以及传递模型
//
//  JXStatusCell.m
//  JX非等高cell
//
//  Created by yuezuo on 16/4/19.
//  Copyright © 2016年 yuezuo. All rights reserved.
//

#import "JXStatusCell.h"
#import "JXStatus.h"

@interface JXStatusCell ()
@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 JXStatusCell

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


- (void)setStatus:(JXStatus *)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.iconView.image = [UIImage imageNamed:status.icon];
    self.nameLabel.text = status.name;
    self.contentLabel.text = status.text;
    if (status.picture) {
        self.pictureView.hidden = NO;
        self.pictureView.image = [UIImage imageNamed:status.picture];
    } else {
        self.pictureView.hidden = YES;
    }
}

- (CGFloat)height {// 返回高度
    if (self.pictureView.hidden) { // 如果配图隐藏,说明没有配图
        return CGRectGetMaxY(self.contentLabel.frame) + 10;
    } else {
        return CGRectGetMaxY(self.pictureView.frame) + 10;
    }
}
@end
- 4.在控制器中设置代码
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    JXStatusCell * cell = [JXStatusCell cellWithTableView:tableView];
    cell.status = self.statusArray[indexPath.row];
    // 强制布局 (如果调用的话系统不会显示label等控件,那么高度就不为空)
    [cell layoutIfNeeded];
    // 存放高度
    self.heights[@(indexPath.row)] = @(cell.height);
    return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    return [self.heights[@(indexPath.row)] doubleValue];
}
/**
 *  性能优化用,返回估计高度
 *  只要设置估计高度 estimatedHeightForRowAtIndexPath,会先调用这个方法之后再调用heightForRowAtIndexPath:方法
 */
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 200;
}

当没有设置估计高度方法的时候系统在显示cell的时候回先调用返回高度方法,这样就不能拿到cell高度 非等高cell的代码源码非等高cell源码

  • 以上代码我们需要在控制器中实现高度,并调用我们可以改进代码,将高度直接写进模型中的一个属性值,在自定义cell中,调用之后就直接将高度存储
//
//  JXStatusCell.m
//  JX非等高cell
//
//  Created by yuezuo on 16/4/19.
//  Copyright © 2016年 yuezuo. All rights reserved.
//

#import "JXStatusCell.h"
#import "JXStatus.h"

@interface JXStatusCell ()
@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 JXStatusCell

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

// 这个功能主要是当直接约束的时候,label计算可能会有点问题,在显示的时候label下的空白比设置的更多
- (void)awakeFromNib {
    // 设置label的每一行的真实宽度
    self.contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20 ;
}

- (void)setStatus:(JXStatus *)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.iconView.image = [UIImage imageNamed:status.icon];
    self.nameLabel.text = status.name;
    self.contentLabel.text = status.text;
    if (status.picture) {
        self.pictureView.hidden = NO;
        self.pictureView.image = [UIImage imageNamed:status.picture];
    } else {
        self.pictureView.hidden = YES;
    }

    // 强制布局
    [self layoutIfNeeded];

    if (self.pictureView.hidden) { // 如果配图隐藏,说明没有配图
        status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10;
    } else {
        status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10;
    }
}

@end
在控制器中调用的方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    JXStatusCell * cell = [JXStatusCell cellWithTableView:tableView];
    cell.status = self.statusArray[indexPath.row];
    return cell;
}


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    JXStatus * cell = self.statusArray[indexPath.row];
    return cell.cellHeight;
}
/**
 *  性能优化用,返回估计高度
 *  只要设置估计高度 estimatedHeightForRowAtIndexPath,会先调用这个方法之后再调用heightForRowAtIndexPath:方法
 */
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 200;
}

优化后代码源码优化后源码

  • storyboard自定义cell
  • 代码自定义cell

补充

self.tableView.tableHeaderView

设置tableView的表头图片,只需要象征性的设置一个一个宽度,系统会自动设置他的宽度。

self.tableView.tableFooterView

以上两种都会随着tableView的滚动滚动

results matching ""

    No results matching ""