UIKit框架(21)UITableView实现复杂单元格(一)

上篇文章介绍了UITableView的数据源驱动、重用机制、刷新数据等基本用法

本篇文章介绍如何实现一些复杂的单元格

UITableViewCell单元格对象有四种基本样式,开发中这个基本样式往往不能满足我们的需求,也就是说需要自定义UITableViewCell的样式,介绍主要的两种做法:

1)使用纯代码自定义

2)使用storyboard中的prototype cell

先来介绍一下UITableViewCell

  • UITableViewCell表格单元格

UITableView中的单元格使用UITableViewCell视图对象

创建方法:

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier

style参数:cell的预设样式

typedef enum : NSInteger {
     UITableViewCellStyleDefault,
     UITableViewCellStyleValue1,
     UITableViewCellStyleValue2,
     UITableViewCellStyleSubtitle
} UITableViewCellStyle;

reuseIdentifier参数:cell的重用ID

单元格的内部视图:

@property(nonatomic, readonly, retain) UIView *contentView

内部视图中又包含以下三个子视图,这三个子视图在不同样式中显示的个数及样式不同

@property(nonatomic, readonly, retain) UILabel *textLabel
@property(nonatomic, readonly, retain) UILabel *detailTextLabel
@property(nonatomic, readonly, retain) UIImageView *imageView

单元格的其他组件:

@property(nonatomic) UITableViewCellAccessoryType accessoryType //右侧指示视图样式
@property(nonatomic, retain) UIView *accessoryView //自定义右侧指示视图

单元格的选择状态:

@property(nonatomic, getter=isSelected) BOOL selected
@property(nonatomic) UITableViewCellSelectionStyle selectionStyle
  • 复杂单元格实现(一): 纯代码自定义

实现如下效果:

实现一个游戏人物信息的展示

分析:

UITableView使用典型的MVC设计模式进行开发

a. 模型:游戏英雄信息数据

b. 视图:单元格视图UITableViewCell的子类

c. 控制器:管理模型数组,以及实现tableView的数据源、代理等

1)模型定义

@interface AMHeroModel : NSObject

@property (nonatomic, copy) NSString * name;    //英雄名

@property (nonatomic, copy) NSString * title;    //英雄称号

@property (nonatomic, copy) NSString * icon;    //英雄图标

@property (nonatomic, copy) NSString * desc;    //英雄描述

@property (nonatomic, copy) NSString * tags;    //英雄标签

+ (AMHeroModel*) modelWithDict:(NSDictionary*)dict;//构造方法:字典转模型

@end

2)控制器管理模型数组并使用懒加载

@interface ViewController ()
@property (nonatomic, strong) NSMutableArray * heroArray;//模型数组
@property (weak, nonatomic) IBOutlet UITableView *tableView;//tableView
@end
#pragma mark - heroArray懒加载
- (NSMutableArray *)heroArray
{
    if ( _heroArray == nil ) {
        NSString * plistPath = [[NSBundle mainBundle] pathForResource:@"hero.plist" ofType:nil];
        NSArray * plistArray = [NSArray arrayWithContentsOfFile:plistPath];
        _heroArray = [NSMutableArray array];
        for ( NSDictionary * dict in plistArray ) {
            AMHeroModel * model = [AMHeroModel modelWithDict:dict];
            [_heroArray addObject:model];
        }
    }
    return _heroArray;
}

3)自定义UITableViewCell子类

需要展示的模型数据包括:4个字符串、一张图片,故UITableViewCell内部应自定义4个UILabel以及一个UIImageView子视图。

实现需要三步:

@interface AMHeroCell : UITableViewCell
//1.添加一个类方法,获取cell
+ (AMHeroCell *) cellWithTableView:(UITableView * ) tableView;
//2.添加模型属性并重写setter方法
@property (nonatomic, strong) AMHeroModel * heroModel;
//3.提供一个类方法,返回cell的高度
+ (CGFloat) cellHeight;
@end

4)UITableViewCell子类:添加一个类方法获取cell

这一步是自定义UITableViewCell最关键且最复杂的步骤,需要完成:

将cell创建/获取的代码封装、创建所有的子视图、设置所有的子视图的frame

//1.1 实现类方法,获取cell
+ (AMHeroCell *)cellWithTableView:(UITableView *)tableView
{
    AMHeroCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if ( cell == nil ) {
        cell = [[AMHeroCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    return cell;
}

创建所有的子视图操作应当放在initWithStyle方法中,故重写

//1.2 重写initWithStyle:方法,添加自定义子视图的代码
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if ( self = [super initWithStyle:style reuseIdentifier:reuseIdentifier] ) {
        //1.2.1 将没有用的子视图删除
        [self.imageView removeFromSuperview];
        [self.textLabel removeFromSuperview];
        [self.detailTextLabel removeFromSuperview];
        //1.2.2 创建自定义的子视图并进行一次性的属性设置
        UIImageView * iconImageView = [[UIImageView alloc] init];
        self.iconImageView  =iconImageView;
        [self.contentView addSubview:self.iconImageView];
        UILabel * nameLabel = [[UILabel alloc] init];
        self.nameLabel = nameLabel;
        [self.contentView addSubview:self.nameLabel];
        self.nameLabel.textColor = [UIColor purpleColor];
        self.nameLabel.font = [UIFont systemFontOfSize:16];
        UILabel * titleLabel = [[UILabel alloc] init];
        self.titleLabel = titleLabel;
        [self.contentView addSubview:self.titleLabel];
        self.titleLabel.textColor = [UIColor grayColor];
        self.titleLabel.font = [UIFont systemFontOfSize:14];
        UILabel * tagsLabel = [[UILabel alloc] init];
        self.tagsLabel = tagsLabel;
        [self.contentView addSubview:tagsLabel];
        self.tagsLabel.textColor = [UIColor redColor];
        self.tagsLabel.font = [UIFont systemFontOfSize:12];
        UILabel * descLabel = [[UILabel alloc] init];
        self.descLabel = descLabel;
        [self.contentView  addSubview:descLabel];
        self.descLabel.textColor = [UIColor blueColor];
        self.descLabel.font = [UIFont systemFontOfSize:12];
        self.descLabel.numberOfLines = 0;
    }
    return self;
}

考虑到屏幕适配,使用设定frame的方式且当前为view子类,所以重写layoutSubviews方法

//1.3 设置所有子视图的frame或者使用autolayout
//如果使用autolayout,代码放在重写的initWithStyle方法中
//如果使用frame,放在layoutSubviews方法中
- (void)layoutSubviews
{
    [super layoutSubviews];
    CGFloat Xspace = self.contentView.frame.size.width*0.05;
    CGFloat Yspace = self.contentView.frame.size.height*0.05;
    
    CGFloat iX, iY, iW, iH;
    iX = Xspace;
    iY = Yspace;
    iH = self.contentView.frame.size.height*0.9;
    iW = iH;
    self.iconImageView.frame = CGRectMake(iX, iY, iW, iH);
    
    CGFloat nX, nY, nW, nH;
    nX = 2*Xspace+iW;
    nY = iY;
    nW = (self.contentView.frame.size.width-3*Xspace-iW-Xspace)/2;
    nH = self.contentView.frame.size.height*0.2;
    self.nameLabel.frame = CGRectMake(nX, nY, nW, nH);
    
    CGFloat tX, tY, tW, tH;
    tX = CGRectGetMaxX(self.nameLabel.frame)+Xspace;
    tY = nY;
    tW = nW;
    tH = nH;
    self.titleLabel.frame = CGRectMake(tX, tY, tW, tH);
    
    CGFloat taX, taY, taW, taH;
    taX = nX;
    taY = self.contentView.frame.size.height*0.3;
    taW = self.contentView.frame.size.width- 3*Xspace -iW;
    taH = nH;
    self.tagsLabel.frame = CGRectMake(taX, taY, taW, taH);
    
    CGFloat dX, dY, dW, dH;
    dX = nX;
    dY = self.contentView.frame.size.height*0.55;
    dW = taW;
    dH = self.contentView.frame.size.height*0.4;
    self.descLabel.frame = CGRectMake(dX, dY, dW, dH);
    
}

5)UITableViewCell子类:添加模型属性并重写setter方法

在3)中的代码示例中已经看到添加了模型属性

重写setter方法的目的是:对外隐藏子视图,数据显示到子视图的操作封装在setter方法内部

- (void)setHeroModel:(AMHeroModel *)heroModel
{
    _heroModel = heroModel;
    //图片对象的创建:两种方式:
    //1)imageNamed 有缓存的图片对象创建方式  以空间换时间
    //self.iconImageView.image = [UIImage imageNamed:_heroModel.icon];
    //2)withContentOfFile方式创建的 不使用缓存  以时间换空间
    NSString * path = [[NSBundle mainBundle] pathForResource:_heroModel.icon ofType:nil];
    self.iconImageView.image = [[UIImage alloc] initWithContentsOfFile:path];
    self.nameLabel.text = _heroModel.name;
    self.titleLabel.text = _heroModel.title;
    self.tagsLabel.text = _heroModel.tags;
    self.descLabel.text = _heroModel.desc;
}

6)UITableViewCell子类:类方法返回cell的高度

UITableView中单元格的高度,默认是44,添加一个类方法返回指定的高度,这样做的好处是:当需求有变时,只需要修改UITableViewCell的子类,即高内聚低耦合的编程思想。

+ (CGFloat)cellHeight
{
    return 140.f;
}

7)控制器实现tableView的数据源、代理方法

#pragma mark - tableView的数据源和代理
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.heroArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [AMHeroCell cellWithTableView:tableView];
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    AMHeroCell * heroCell = cell;
    
    heroCell.heroModel = self.heroArray[indexPath.row];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [AMHeroCell cellHeight];
}

可以看到数据源、代理方法的实现极其简单,且无论模型数据怎么变,cell怎么变,这部分的代码是几乎不变的

  • 案例的额外功能扩展

1)添加功能

从截图可以看到,导航栏有一个加号按钮,实现响应方法:随机添加一个英雄

#pragma mark - 添加cell
- (void) addBarBtnClicked
{
    NSInteger index = random()%self.heroArray.count;
    AMHeroModel * model = self.heroArray[index];
    [self.heroArray insertObject:model atIndex:0];
    [self.tableView reloadData];
}

2)删除功能

从截图可以看到,导航栏有一个删除按钮,点击后UITableView进入编辑状态

#pragma mark - 删除cell
- (void) delBarBtnClicked
{
    [self.tableView setEditing:!self.tableView.isEditing animated:YES];
}

点击编辑状态的删除按钮的响应方法

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{//当进入编辑状态后,点击delete按钮时按钮
    [self.heroArray removeObjectAtIndex:indexPath.row];
    [self.tableView reloadData];
}

3)点击一个cell,弹出一个UIAlertController,可以进行简单的数据修改

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIAlertController * ac = [UIAlertController alertControllerWithTitle:@"修改英雄信息" message:@"输入修改该的数据" preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction * a1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        
    }];
    UIAlertAction * a2 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        model.name = ac.textFields[0].text;
        model.title = ac.textFields[1].text;
        [self.tableView reloadData];//重新加载数据
    }];
    [ac addAction:a1];
    [ac addAction:a2];
    [ac addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = model.name;
    }];
    [ac addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = model.title;
    }];
    
    [self presentViewController:ac animated:YES completion:nil];
}

效果:

时间: 2024-10-13 10:21:54

UIKit框架(21)UITableView实现复杂单元格(一)的相关文章

iOS开发——UI_swift篇&UItableView实现移动单元格

UItableView实现移动单元格 1,下面的样例是给表格UITableView添加单元格移动功能: (1)给表格添加长按功能,长按后表格进入编辑状态 (2)在编辑状态下,可以看到单元格后面出现拖动按钮 (3)鼠标按住拖动按钮,可以拖动单元格到任意位置 (4)拖动完毕后,还会触发TabelView对应的代理事件 2,效果图如下:   3,代码如下 1 import UIKit 2 3 class ViewController: UIViewController,UITableViewDeleg

UIKit框架(22)UITableView之静态单元格

表格控制器 UITableViewController是UIViewController的子类 控制器中包含一个UITableView视图属性: @property(nonatomic, retain) UITableView *tableView UITableViewController遵循UITableView的数据源协议.代理协议 并且属性tableView的数据源.代理均被设置为控制器. 表格视图的刷新控件: @property(nonatomic, retain) UIRefresh

UIKit 框架之UITableView二

// // ViewController.m // UITableView // // Created by City--Online on 15/5/21. // Copyright (c) 2015年 XQB. All rights reserved. // #import "ViewController.h" @interface ViewController ()<UITableViewDataSource,UITableViewDelegate> @propert

iOS UITableView 移除单元格选中时的高亮状态

郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 假设文章对您有所帮助.欢迎给作者捐赠.支持郝萌主,捐赠数额任意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 在处理UITableView表格时,我们希望用户可以和触摸单元格式进行交互. 可是希望用户在完毕交互之后,这些单元格的选中状态可以消失. Cocoa Touch 提供了两种方法来防止单元格背持久选中. 1.cell.selectionStyle = UITableViewCellSelectionStyleN

UITableView&amp;UICollectionView设置单元格的默认选中状态

1. 场景需求 一个表格视图(或者宫格视图)中,当一个单元格被选中时设置彩色样式,选中其它单元格时设置灰色样式. 2. 一个思路 通过实现选中和非选择的代理,以在适当的时机进行UI更新操作. 3. UITableView 3.1 通过屏幕点击改变的选中状态回调给代理 //选中-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath;//非选中-(void)tableView:(

【ios学习记录】-如何定制UITableView的圆角单元格

自从ios7更新以来,UITableView控件的边角style由默认圆角变成了直角,更加适应UI扁平化设计的效果了.但对于某种情况来说,如果tableview宽度不是拉伸到与父视图等宽,那么使用直角的tableview则会显得不好看.如下图分组列表(group tableview)所示. 而如果此时采用圆角效果的话,则会显示圆润温和,用户会觉得好看,体验很好.如下图所示. 要实现以上分组列表(group tableview)的圆角效果,主要是通过Core Graphics API来实现图层重绘

TableView静态单元格实现

整理TableView静态单元格的使用方法 需要实现的页面: 基本框架就是四个静态单元格,在此整理一下TableView的使用细节. 1.首先创建一个group,此内有两个类,一个是AccountBoundingViewController,继承自UIViewController:一个是AccountBoundingViewCell,继承自UITableViewCell. 2.先在xib中构造cell,内容如下: 由一个ImageView,一个Label,一个Button组成(在此并没有设定co

iOS开发UI篇—在UITableview的应用中使用动态单元格来完成app应用程序管理界面的搭建

一.实现效果 说明:该示例在storyboard中使用动态单元格来完成. 二.实现 1.项目文件结构和plist文件 2.实现过程以及代码 在tableview的属性选择器中选择动态单元格. 说明:在storyboard中直接使用其自带的动态单元格完成tableviewcell的定义,并创建了一个管理该cell的类,进行了连线. 实现代码: 数据模型部分: YYappInfo.h文件 1 // 2 // YYappInfo.h 3 // 01-使用动态单元格来完成app应用程序管理界面的搭建 4

iOS开发——UI_swift篇&amp;UITableView实现单元格展开与隐藏

UITableView实现单元格展开与隐藏 下面是一个列表单元格cell的折叠展开效果的demo. 当点击单元格时会展开该单元格,便于显示一些详情什么的.点击其他单元格原来的会关闭,同时有动画效果. 效果如如下:   代码如下: 1 import UIKit 2 3 class ViewController: UIViewController,UITableViewDelegate, 4 UITableViewDataSource { 5 6 var tableView:UITableView?