UI基础-UITableView 高级

自定义cell

概要:

自定义cell就是创建一个UITableViewCell的子类。
把cell上的控件创建都封装在子类中,简化UIViewController中的代 码。
子视图控件添加到cell的contentView上。

通信

cell中声明一个Model类型的属性,viewController中获取到Model对象后赋值给cell的Model属性。
cell中重写Model的setter方法,把Model对象中的内容重新赋值给各
个控件 M和V不直接进行通信,C负责M和V之间进行通信。

创建新工程,添加加载数据方法和创建TableView

// 加载数据
- (void)setUpData
{
    // 获取文件路径
    NSString *path = [[NSBundle mainBundle] pathForResource:@"StudentList" ofType:@"plist"];
    // 从该路径 读取文件
    NSArray *arr = [NSArray arrayWithContentsOfFile:path];

    // 字典转化成模型  再放入字典或数组
    self.dataArr = [NSMutableArray array];

    for (NSDictionary *dic in arr) {
        CellModel *model = [[CellModel alloc] init];
        [model setValuesForKeysWithDictionary:dic];
        [self.dataArr addObject:model];
        [model release];
    }
}
- (void)addTableView
{
    UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
    tableView.dataSource = self;
    tableView.delegate = self;
    [self.view addSubview:tableView];
    [tableView release];
}

自定义cell的步骤:

创建TableViewCell的子类

创建2个名为MyTableViewCell、ManTableViewCell的继承于TableViewCell的子类
这里写图片描述

这里写图片描述

重写初始化方法

MyTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self addSubView];
    }
    return self;
}
ManTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self addSubview];
    }
    return self;
}

把要添加的控件 添加在cell的实现内容区域 contentView上面

MyTableViewCell
// 添加子视图
- (void)addSubView
{
    self.imageV = [[UIImageView alloc] initWithFrame:CGRectMake(kMargin, kMargin, kImageWidth, kImageHeight)];
    self.imageV.backgroundColor = [UIColor redColor];
    [self.contentView addSubview:self.imageV];
    [self.imageV release];

    self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.imageV.right + kMargin, self.imageV.top, (kScreenWidth - 3*kMargin - self.imageV.width), (kImageHeight - 2*kLabelMargin)/3)];
    self.nameLabel.backgroundColor = [UIColor greenColor];
    [self.contentView addSubview:self.nameLabel];
    [self.nameLabel release];

    self.phoneLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.nameLabel.left, self.nameLabel.bottom + kLabelMargin, self.nameLabel.width, self.nameLabel.height)];
    self.phoneLabel.backgroundColor = [UIColor greenColor];
    [self.contentView addSubview:self.phoneLabel];
    [self.phoneLabel release];

    self.genderLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.phoneLabel.left, self.phoneLabel.bottom + kLabelMargin, self.phoneLabel.width, self.phoneLabel.height)];
    self.genderLabel.backgroundColor = [UIColor greenColor];
    [self.contentView addSubview:self.genderLabel];
    [self.genderLabel release];

}
ManTableViewCell
- (void)addSubview
{
    self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
    self.nameLabel.backgroundColor = [UIColor orangeColor];
    [self.contentView addSubview:self.nameLabel];
    [self.nameLabel release];
}

把系统的cell 替换成 自定义cell完成

MyTableViewCell
 static NSString *identifier = @"GirlCell";
 MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[[MyTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
ManTableViewCell
static NSString *identifier = @"ManCell";
ManTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[[ManTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];

多种类型的cell混合使用

注意:

通常我们会在tableView:cellForRowAtIndexPath:方法中根据不同的。
Model来决定使用什么类型的cell 每种类型的cell要定义不同的重用标识符 。
cell重用的时候会根据重用标识从重用队列中取用哪种类型的cell。

新建一个cellModel继承于NSObject,声明它的属性

// 名字
@property (nonatomic, retain) NSString *name;
// 性别
@property (nonatomic, retain) NSString *gender;
// 手机号
@property (nonatomic, retain) NSString *phoneNumber;

把它声明成tableViewcell子类的属性,见上文截图中。

用model在- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法中判断用哪种cell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 根据model的值进行判断 显示不同的cell
    CellModel *model = self.dataArr[indexPath.row];
    if ([model.gender isEqualToString:@"女"]) {
        static NSString *identifier = @"GirlCell";
        MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
        if (cell == nil) {
            cell = [[[MyTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
        }
        // 在给model赋值的同时 咱们希望  也给cell上控件完成赋值
        cell.model = model;
        return cell;
    } else {
        static NSString *identifier = @"ManCell";
        ManTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
        if (cell == nil) {
            cell = [[[ManTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
        }
        cell.model = model;
        return cell;
    }

}

这样就能达到下面的效果了
这里写图片描述

cell自适应高度

获取文本高度:(iOS7)

- (CGRect)boundingRectWithSize:(CGSize)size options:
(NSStringDrawingOptions)options attributes:(NSDictionary
*)attributes context:(NSStringDrawingContext *)context

注意
计算文本高度是所用的字体要和label显示时用的字体一致。
label的宽度要和计算时使用的限定宽度一致。
这样才能保证文本显示在label中时,label高度恰巧够。

例题:实现如下效果,label正好是适应文字大小

这里写图片描述

新建工程,创建一个UIlabel

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 300, 100)];
label.backgroundColor = [UIColor orangeColor];
label.font = [UIFont systemFontOfSize:16];
label.text = @" 我崇拜高尚的生命的秘密。我崇拜这生命在降生、成长、战斗、伤残、牺牲时迸溅出的钢花焰火。我崇拜一个活灵灵的生命在崇山大河,在海洋和大陆上飘荡的自由。是的,生命就是希望。它飘荡无定,自由自在,它使人类中总有一支血脉不甘于失败,九死不悔地追寻着自己的金牧场。";
[self.window addSubview:label];
[label release];

构建字体大小的字典 UIFont,NSFontAttributeName UIFont 在系统中的属性的名字(key)

    // 参数size
    // 宽度 跟你的label一边宽
    // 高度 填一个这段字最大高度
    // CGFLOAT_MAX 最大的float数
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:16], NSFontAttributeName, nil];
    CGRect frame = [label.text boundingRectWithSize:CGSizeMake(300, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) attributes:dic context:nil];
    NSLog(@"%f", frame.size.height);

    // 更改label的高度
    CGRect temp = label.frame;
    temp.size.height = frame.size.height;
    label.frame = temp;

    label.numberOfLines = 0;
    [self.window addSubview:label];
    [label release];

这样就能简单实现label适应文字的大小了
那怎么让cell自适应高度呢?怎么解决cell的复用问题呢?

例题:

新建工程,添加加载数据方法,添加表视图

- (void)setUpData
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"NewsData" ofType:@"plist"];
    NSDictionary *dicData = [NSDictionary dictionaryWithContentsOfFile:path];

    self.dataArr = [NSMutableArray array];
    NSArray *newsArr = dicData[@"news"];
    for (NSDictionary *dic in newsArr) {
        NewsModel *model = [[NewsModel alloc] init];
        [model setValuesForKeysWithDictionary:dic];
        // 给点击状态赋值初值
        model.isSelect = NO;
        [self.dataArr addObject:model];
        [model release];
    }
}

- (void)addTableView
{
    UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
    tableView.dataSource = self;
    tableView.delegate = self;
    [self.view addSubview:tableView];
    [tableView release];
}

创建NewsModel,声明属性

@property (nonatomic, retain) NSString *title;
@property (nonatomic ,retain) NSString *summary;

// 标识选中的状态
@property (nonatomic, assign) BOOL isSelect;

创建NewsTableViewCell,重写初始化方法,添加视图

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self addSubview];
    }
    return self;
}

- (void)addSubview
{
    self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(kMargin, kMargin, kLabelWidth, kLabelHeight)];
    self.titleLabel.backgroundColor = [UIColor cyanColor];
    [self.contentView addSubview:self.titleLabel];
    [self.titleLabel release];

    self.summaryLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.titleLabel.left, self.titleLabel.bottom + kMargin, self.titleLabel.width, self.titleLabel.height)];
    self.summaryLabel.backgroundColor = [UIColor orangeColor];
    // 多行显示
    self.summaryLabel.numberOfLines = 0;
    // 设置字体大小
    self.summaryLabel.font = [UIFont systemFontOfSize:16];
    [self.contentView addSubview:self.summaryLabel];
    [self.summaryLabel release];

    self.imageV = [[UIImageView alloc] initWithFrame:CGRectMake(self.titleLabel.right + kMargin, kMargin, 3 * kMargin, 3 * kMargin)];
    [self.contentView addSubview:self.imageV];
    [self.imageV release];
}

实现协议中的两个方法

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"MyCell";
    NewsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (cell == nil) {
        cell = [[[NewsTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
    }
    NewsModel *model = self.dataArr[indexPath.row];
    cell.model = model;
    return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.dataArr.count;
}

实现cell的点击方法

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 取出点击 cell对应的model
    NewsModel *model = self.dataArr[indexPath.row];

    // 取出被点击的cell
    NewsTableViewCell *cell = (NewsTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];

    // 更改点击的状态
    model.isSelect = !model.isSelect;
    if (model.isSelect == YES) {
        // 更改cell上的图片
        cell.imageV.image = [UIImage imageNamed:@"select"];
    } else {
        // 更改cell上的图片
        cell.imageV.image = [UIImage imageNamed:@"cancel"];
    }

}

重写model的setter方法

- (void)setModel:(NewsModel *)model
{
    if (_model != model) {
        [_model release];
        _model = [model retain];
    }
    self.titleLabel.text = model.title;
    self.summaryLabel.text = model.summary;

    // 利用model中的点选状态 解决cell复用问题
    // 需要每次被复用的cell 再进行一次与状态对应的赋值
    if (model.isSelect == YES) {
        self.imageV.image = [UIImage imageNamed:@"select"];
    } else {
        self.imageV.image = [UIImage imageNamed:@"cancel"];
    }

    // 获取字符串的高度
    CGFloat summaryHeight = [NewsTableViewCell cellHeightForModel:model];
    // 改变一下label的高度
    CGRect frame = self.summaryLabel.frame;
    frame.size.height = summaryHeight;
    self.summaryLabel.frame = frame;

}

计算高度的类方法

+ (CGFloat)cellHeightForModel:(NewsModel *)model
{
  // 创建字体大小的字典
    NSDictionary *fontDic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};
    // 计算字符串高度
    CGRect textRect = [model.summary boundingRectWithSize:CGSizeMake(kLabelWidth, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) attributes:fontDic context:nil];
    return textRect.size.height;
}

返回cell的动态高度

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NewsModel *model = self.dataArr[indexPath.row];
    CGFloat labelHeight = [NewsTableViewCell cellHeightForModel:model];

    // 上边距 + topLabel + 中间间距 + 动态label高度 + 下边距
    return 20 + labelHeight + 40 + 10;
}

这里写图片描述
这样就解决了cell的自适应高度和cell的复用问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值