美文网首页
iOS开发之UI篇(5)—— UITableView

iOS开发之UI篇(5)—— UITableView

作者: fengfeng427 | 来源:发表于2019-06-19 00:06 被阅读0次

    一.基本概念

    UITableView : UIScrollView : UIView : UIResponder : NSObject

    从继承关系我们可以知道:UITableView可以滚动,具有view的性质,能响应UI事件等等


    1. UITableView结构:

    UITableView可以有很多个section(本文成为组),每个section分别由一个Header(组头)、一个Fooder(组尾)及若干个cell组成。


    2. UITableView有两种风格:

    typedef NS_ENUM(NSInteger, UITableViewStyle) {

        UITableViewStylePlain,

        UITableViewStyleGrouped

    };

    两种风格主要区别为:

    没有设置Header(或Fooder)时,Plain风格会隐藏掉Header(或Fooder),而Grouped仍会显示一个默认高度值。


    3. UITableViewCell有四种风格

    typedef NS_ENUM(NSInteger, UITableViewCellStyle) {

        UITableViewCellStyleDefault,   

        UITableViewCellStyleValue1,   

        UITableViewCellStyleValue2,   

        UITableViewCellStyleSubtitle   

    };

    如果你的UITableViewCell不显示image或者detailTextLabel,那么最好先检查一下注册的时候设置的style是否符合。


    4. 重用cell

    为什么要重用cell?

    由于屏幕显示的cell有限,而当数据量大的时候,如果每个数据创建一个Cell,就会占很大内存。

    重用cell机制是这样的:创建了屏幕内cell所需的个数+1个屏幕之外的cell,在满足显示效果的前提下尽可能降低消耗内存。

    这种机制下默认有一个可变数组NSMutableArray* visiableCells用来保存当前显示的cell一个可变字典NSMutableDictionary* reusableTableCells用来保存可重复利用的cell(之所以用字典是因为可重用的cell有不止一种样式,我们需要根据它的reuseIdentifier,也就是所谓的重用标示符来查找是否有可重用的该样式的cell)

    如何重用cell?

    首先要创建一个有重用标识符reuseIdentifier的cell,这些cell会被保存到重用队列中去。当需要实例化cell的时候,首先根据reuseIdentifier去找对应的cell。

    加载cell到重用队列有两种方法:

    // 法1:使用tableView注册一个cell

    [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"myCell"];

    // 法2:在cell的配置代理方法创建一个cell

    if (cell == nil) {

            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];

    }

    根据重用标识符reuseIdentifier实例化一个cell:

    // 实例化一个UITableViewCell

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myCell" forIndexPath:indexPath];



    二、简单用法

    代码实例:

    #define    TABLE_WIDTH      [UIScreen mainScreen].bounds.size.width

    #define    TABLE_HEIGHT    [UIScreen mainScreen].bounds.size.height

    #import "ViewController.h"

    @interface ViewController () <UITableViewDelegate, UITableViewDataSource>

    @property (nonatomic, strong) UITableView *tableView;

    @property (nonatomic, strong, nullable) NSArray *digitalArr;

    @property (nonatomic, strong, nullable) NSArray *letterArr;

    @end

    @implementation ViewController

    - (void)viewDidLoad {

        [super viewDidLoad];

        // 装载数据源

        self.digitalArr = @[@"0", @"1", @"2"];

        self.letterArr  = @[@"A", @"B", @"C"];

        // 添加tableView到self.view

        [self.view addSubview:self.tableView];

    }

    - (void)didReceiveMemoryWarning {

        [super didReceiveMemoryWarning];

    }

    #pragma mark - UITableViewDelegate

    // cell高度

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

        return 60;

    }

    // Header高度

    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {

        return 30;

    }
    // Footer高度

    - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {

        return 30;

    }

    // 选中了某个cell

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    }

    #pragma mark - UITableViewDataSource

    // Section数量

    -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

        return 2;

    }

    // 对应Section中cell的个数

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

        switch (section) {

            case 0:    // 数字组

                return self.digitalArr.count;

                break;

            case 1:    // 字母组

                return self.letterArr.count;

                break;

            default:

                break;

        }

        return 0;

    }

    // Header标题

    - (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

        switch (section) {

            case 0:    // 数字组

                return @"数字组头";

                break;

            case 1:    // 字母组

                return @"字母组头";

                break;

            default:

                break;

        }

        return nil;

    }

    // Footer标题

    - (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {

        switch (section) {

            case 0:    // 数字组

                return @"数字组尾";

                break;

            case 1:    // 字母组

                return @"字母组尾";

                break;

            default:

                break;

        }

        return nil;

    }

    // cell

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

        // 实例化一个UITableViewCell

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myCell" forIndexPath:indexPath];

        // 由于已经注册过重用cell,所以这里不必判断是否存在

    //    if (cell == nil) {

    //        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];

    //    }

        // 设置cell相关属性

        switch (indexPath.section) {

            case 0:    // 数字组

                if (indexPath.row < self.digitalArr.count) {

                    cell.textLabel.text = self.digitalArr[indexPath.row];

                }

                break;

            case 1:    // 字母组

                if (indexPath.row < self.letterArr.count) {

                    cell.textLabel.text = self.letterArr[indexPath.row];

                }

                break;

            default:

                break;

        }

        // 返回cell

        return cell;

    }

    #pragma mark - 懒加载

    - (UITableView *)tableView {

        if (_tableView == nil) {

          // 实例化一个UITableView

            _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, TABLE_WIDTH, TABLE_HEIGHT)

                                                      style:UITableViewStylePlain];

            // 注册一个cell

            [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"myCell"];

            // 设置代理

            _tableView.delegate = self;

            _tableView.dataSource = self;

        }

        return _tableView;

    }

    @end



    三、cell的增、减、排序

    当然,这里要说的操作并不是先改变数据源然后reloadData,而是通过对cell的操作,使整个过程具有动画效果、显得顺畅。

    1. 添加

    我们会新增两个代理,第一个是返回编辑操作的类型。有插入类型删除类型

    先定义一个全局变量_isDelete用于标记是点击添加还是删除按钮:

    - (IBAction)btnAdd:(UIButton *)sender {

        _isDelete = NO;    // 编辑类型:添加

        // 启动编辑(参数一:tableView是否正在编辑;参数二:是否动画)

        [self.tableView setEditing:!self.tableView.isEditing animated:YES];

    }

    调用方法setEditing: animated:后,接着来到代理方法:

    // 返回操作类型(添加/删除)

    - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {

        NSLog(@"返回操作类型");

        if (_isDelete) {

            return UITableViewCellEditingStyleDelete;

        }

        return UITableViewCellEditingStyleInsert;

    }

    当点击左边绿色“+”号按钮后,调用此代理方法: // 提交编辑操作

    - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

        NSLog(@"提交编辑操作");

        if (editingStyle == UITableViewCellEditingStyleInsert) {

            switch (indexPath.section) {

                case 0:

                    [self.digitalArr insertObject:@"我是新插入的数字" atIndex:indexPath.row];

                    break;

                case 1:

                    [self.letterArr insertObject:@"我是新插入的字母" atIndex:indexPath.row];

                    break;

                default:

                    break;

            }

            // 与reloadData不同,此方法带动画效果.(参数二为动画类型)

            [tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];

        }

    }


    2. 删除

    与添加类似,假如我们设置_isDelete = YES,那么代理返回的是UITableViewCellEditingStyleDelete类型,这时删除是这样子的:

    你也不喜欢这种做法对不对?那我们换个做法,直接左滑删除某个cell或者点编辑多选/全选然后批量删除cell。


    滑动删除

    其实,只要你返回UITableViewCellEditingStyleDelete类型,并实现代理方法tableView : commitEditingStyle: forRowAtIndexPath:,然后就可以向左滑动调出删除按钮了。但是这样一来点击添加按钮就不能调出绿色“+”号按钮了,要解决这个问题,需要修改之前的返回类型的代理方法:

    // 返回操作类型(添加/删除)

    - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {

        if (!tableView.isEditing) {

            return UITableViewCellEditingStyleDelete;

        }

        return UITableViewCellEditingStyleInsert;

    }

    // 提交编辑操作

    - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

        NSLog(@"提交编辑操作");

        if (editingStyle == UITableViewCellEditingStyleInsert) {

            switch (indexPath.section) {

                case 0:

                    [self.digitalArr insertObject:@"我是新插入的数字" atIndex:indexPath.row];

                    break;

                case 1:

                    [self.letterArr insertObject:@"我是新插入的字母" atIndex:indexPath.row];

                    break;

                default:

                    break;

            }

            // 与reloadData不同,此方法带动画效果.(参数二为动画类型)

            [tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];

        }else if (editingStyle == UITableViewCellEditingStyleDelete) {

            switch (indexPath.section) {

                case 0:

                    if (indexPath.row < self.digitalArr.count) {

                        [self.digitalArr removeObjectAtIndex:indexPath.row];

                    }

                    break;

                case 1:

                    if (indexPath.row < self.letterArr.count) {

                        [self.letterArr removeObjectAtIndex:indexPath.row];

                    }

                    break;

                default:

                    break;

            }

            // 与reloadData不同,此方法带动画效果.(参数二为动画类型)

            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];

        }

    }


    批量删除

    #define    TABLE_WIDTH      [UIScreen mainScreen].bounds.size.width

    #define    TABLE_HEIGHT    [UIScreen mainScreen].bounds.size.height

    #import "ViewController.h"

    @interface ViewController () <UITableViewDelegate, UITableViewDataSource> {

        BOOL    _isDelete;      // 用于区别是添加还是删除操作

        NSMutableArray *_deleteArr;    // 用于储存将要删除的cell的indexPath

    }

    @property (weak, nonatomic) IBOutlet UIButton *editBtn;

    @property (weak, nonatomic) IBOutlet UIButton *addBtn;

    @property (weak, nonatomic) IBOutlet UIView *bottomView;

    @property (weak, nonatomic) IBOutlet UIButton *allBtn;

    @property (weak, nonatomic) IBOutlet UIButton *deleteBtn;

    @property (nonatomic, strong) UITableView *tableView;

    @property (nonatomic, strong, nullable) NSMutableArray *digitalArr;

    @property (nonatomic, strong, nullable) NSMutableArray *letterArr;

    @end

    @implementation ViewController

    - (void)viewDidLoad {

        [super viewDidLoad];

        _isDelete = YES;    // 编辑类型:删除

        self.bottomView.hidden = YES;      //隐藏底部view

        self.deleteBtn.hidden = YES;        //隐藏删除按钮

        // 装载数据源

        self.digitalArr = [@[@"0", @"1", @"2"] mutableCopy];

        self.letterArr  = [@[@"A", @"B", @"C"] mutableCopy];

        // 添加tableView到self.view

        [self.view addSubview:self.tableView];

    }

    - (void)didReceiveMemoryWarning {

        [super didReceiveMemoryWarning];

    }

    #pragma mark - UITableViewDelegate

    // cell高度

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

        return 60;

    }

    // Header高度

    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {

        return 30;

    }

    // Footer高度

    - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {

        return 30;

    }

    // 选中了某个cell

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

        // 判断是否是多选操作

        if (self.tableView.editing) {

            if (_deleteArr == nil) {

                _deleteArr = [NSMutableArray new];

            }

            // 将选中cell的indexPath添加到删除数组deleteArr

            [_deleteArr addObject:indexPath];

            // 显示删除按钮

            self.deleteBtn.hidden = NO;

            return;

        }

    }

    // 取消选中时 将存放在deleteArr中的数据移除

    - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {

        // 判断是否是多选操作

        if (self.tableView.editing) {

            //将取消选中的cell的indexPath从数组deleteArr中移除

            [_deleteArr removeObject:indexPath];

            //隐藏删除按钮

            if ([_deleteArr count] == 0) {

                _deleteBtn.hidden = YES;

            }

        }

    }

    // 返回操作类型(添加/删除)

    - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {

        NSLog(@"返回操作类型");

        if (!tableView.isEditing) {

            return UITableViewCellEditingStyleDelete;

        }

        return UITableViewCellEditingStyleInsert;

    }

    // 提交编辑操作

    - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

        NSLog(@"提交编辑操作");

        if (editingStyle == UITableViewCellEditingStyleInsert) {

            switch (indexPath.section) {

                case 0:

                    [self.digitalArr insertObject:@"我是新插入的数字" atIndex:indexPath.row];

                    break;

                case 1:

                    [self.letterArr insertObject:@"我是新插入的字母" atIndex:indexPath.row];

                    break;

                default:

                    break;

            }

            // 与reloadData不同,此方法带动画效果.(参数二为动画类型)

            [tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];

        }else if (editingStyle == UITableViewCellEditingStyleDelete) {

            switch (indexPath.section) {

                case 0:

                    if (indexPath.row < self.digitalArr.count) {

                        [self.digitalArr removeObjectAtIndex:indexPath.row];

                    }

                    break;

                case 1:

                    if (indexPath.row < self.letterArr.count) {

                        [self.letterArr removeObjectAtIndex:indexPath.row];

                    }

                    break;

                default:

                    break;

            }

            // 与reloadData不同,此方法带动画效果.(参数二为动画类型)

            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];

        }

    }

    #pragma mark - UITableViewDataSource

    // Section数量

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

        return 2;

    }

    // 对应Section中cell的个数

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

        switch (section) {

            case 0:    // 数字组

                return self.digitalArr.count;

                break;

            case 1:    // 字母组

                return self.letterArr.count;

                break;

            default:

                break;

        }

        return 0;

    }

    // Header标题

    - (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

        switch (section) {

            case 0:    // 数字组

                return @"数字组头";

                break;

            case 1:    // 字母组

                return @"字母组头";

                break;

            default:

                break;

        }

        return nil;

    }

    // Footer标题

    - (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {

        switch (section) {

            case 0:    // 数字组

                return @"数字组尾";

                break;

            case 1:    // 字母组

                return @"字母组尾";

                break;

            default:

                break;

        }

        return nil;

    }

    // cell

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

        // 实例化一个UITableViewCell

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myCell" forIndexPath:indexPath];

        // 由于已经注册过重用cell,所以这里不必判断是否存在

    //    if (cell == nil) {

    //        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];

    //    }

        // 设置cell相关属性

        switch (indexPath.section) {

            case 0:    // 数字组

                if (indexPath.row < self.digitalArr.count) {

                    cell.textLabel.text = self.digitalArr[indexPath.row];

                }

                break;

            case 1:    // 字母组

                if (indexPath.row < self.letterArr.count) {

                    cell.textLabel.text = self.letterArr[indexPath.row];

                }

                break;

            default:

                break;

        }

        // 返回cell

        return cell;

    }

    #pragma mark - UI事件

    - (IBAction)btnEdit:(UIButton *)sender {

        // 如果当前有别人的编辑操作,取消别人的编辑操作,并退出

        if (self.tableView.isEditing && !sender.selected) {

            // 启动编辑(参数一:tableView是否正在编辑;参数二:是否动画)

            [self.tableView setEditing:!self.tableView.isEditing animated:YES];

            return;

        }

        // 编辑时显示底部view、不显示添加按钮

        sender.selected = !sender.selected;

        if (sender.selected) {

            self.addBtn.hidden = YES;

            self.deleteBtn.hidden = YES;

            self.deleteBtn.selected = NO;

            self.allBtn.selected = NO;

            self.bottomView.hidden = NO;

        }else {

            // 将要隐藏底部view时,还原各种btn

            self.addBtn.hidden = NO;

            self.deleteBtn.hidden = YES;

            self.deleteBtn.selected = NO;

            self.allBtn.selected = NO;

            self.bottomView.hidden = YES;

        }

        // 支持同时选中多行

        self.tableView.allowsMultipleSelectionDuringEditing = YES;

        // 启动编辑(参数一:tableView是否正在编辑;参数二:是否动画)

        [self.tableView setEditing:!self.tableView.isEditing animated:YES];

    }

    - (IBAction)btnAdd:(UIButton *)sender {

        // 如果当前有别人的编辑操作,取消别人的编辑操作,并退出

        if (self.tableView.isEditing && !sender.selected) {

            // 启动编辑(参数一:tableView是否正在编辑;参数二:是否动画)

            [self.tableView setEditing:!self.tableView.isEditing animated:YES];

            return;

        }

        // 编辑时显示底部view、不显示添加按钮

        sender.selected = !sender.selected;

        if (sender.selected) {

            self.editBtn.hidden = YES;

        }else {

            self.editBtn.hidden = NO;

        }

        _isDelete = NO;    // 编辑类型:添加

        // 取消支持同时选中多行

        self.tableView.allowsMultipleSelectionDuringEditing = NO;

        // 启动编辑(参数一:tableView是否正在编辑;参数二:是否动画)

        [self.tableView setEditing:!self.tableView.isEditing animated:YES];

    }

    - (IBAction)btnAll:(UIButton *)sender {

        sender.selected = !sender.selected;

        if (sender.selected) {

            // 遍历选中数字cell

            for (int i=0; i<self.digitalArr.count; i++) {

                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];

                // 选中TableView中的cell

                [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionTop];

                // 将选中的cell的indexPath存入数组deleteArr

                if (_deleteArr == nil) {

                    _deleteArr = [NSMutableArray new];

                }

                [_deleteArr addObject:indexPath];

            }

            // 遍历选中字母cell

            for (int i=0; i<self.letterArr.count; i++) {

                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:1];

                // 选中TableView中的cell

                [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionTop];

                // 将选中的cell的indexPath存入数组deleteArr

                if (_deleteArr == nil) {

                    _deleteArr = [NSMutableArray new];

                }

                [_deleteArr addObject:indexPath];

            }

            // 显示删除按钮

            self.deleteBtn.hidden = NO;

        }else {

            // 遍历取消选中数字cell

            for (int i=0; i<self.digitalArr.count; i++) {

                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];

                //取消选中TableView中的cell

                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];

            }

            // 遍历取消选中字母cell

            for (int i=0; i<self.letterArr.count; i++) {

                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:1];

                //取消选中TableView中的cell

                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];

            }

            // 移除数组deleteArr中所有元素(indexPath)

            if (_deleteArr) {

                [_deleteArr removeAllObjects];

            }

            //隐藏删除按钮

            self.deleteBtn.hidden = YES;

        }

    }

    - (IBAction)btnDelete:(UIButton *)sender {

        if (self.tableView.isEditing && _deleteArr) {

            // 此处运用i--是为了防止removeObjectAtIndex出错

            for (NSInteger i=_deleteArr.count-1; i>=0; i--) {

                NSIndexPath *indexPath = _deleteArr[i];

                // 数据源同步删除

                switch (indexPath.section) {

                    case 0:    // 数字

                        if (indexPath.row < self.digitalArr.count) {

                            [self.digitalArr removeObjectAtIndex:indexPath.row];

                        }

                        break;

                    case 1:    // 字母

                        if (indexPath.row < self.letterArr.count) {

                            [self.letterArr removeObjectAtIndex:indexPath.row];

                        }

                        break;

                    default:

                        break;

                }

                // 与reloadData不同,此方法带动画效果.(参数二为动画类型)

                [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];

            }

            [_deleteArr removeAllObjects];

        }

    }

    #pragma mark - 懒加载

    - (UITableView *)tableView {

        if (_tableView == nil) {

            // 实例化一个UITableView

            _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, TABLE_WIDTH, TABLE_HEIGHT-64-46)

                                                      style:UITableViewStylePlain];

            // 注册一个cell

            [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"myCell"];

            // 设置代理

            _tableView.delegate = self;

            _tableView.dataSource = self;

        }

        return _tableView;

    }

    @end


    3. 排序

    和增加、删除相似,要实现排序功能只需实现下面这个协议即可:

    /**

    移动cell

    @param tableView 正在操作的tableView对象

    @param sourceIndexPath 移动前的indexPath

    @param destinationIndexPath 移动后的indexPath

    */

    - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {

        // 需要改变数据源的排序

    }


    四、几种方法创建cell

    常用cell的创建方法有两种,一种是使用系统cell,另一种是自定义cell。其中自定义cell又可以分为三种:纯代码创建cell,XIB创建cellstoryboard创建cell


    1. 使用系统cell

    其实我们上面例子中使用的就是系统cell:通过注册一个UITableViewCell,然后在tableView: cellForRowAtIndexPath:方法里返回一个已经注册到重用队列里的UITableViewCell。

    // 注册一个cell

    [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"myCell"];

    // 在tableView: cellForRowAtIndexPath:方法里实例化一个UITableViewCell,并返回

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myCell" forIndexPath:indexPath];

    return cell;


    2. 自定义cell之纯代码

    首先,我们先新建一个继承自UITableViewCell的类MyTableViewCell: 然后导入头文件#import “MyTableViewCell.h”,接下来将上面的系统cell改为我们新建的自定义cell就好了:

    // 注册一个cell

    [_tableView registerClass:[MyTableViewCell class] forCellReuseIdentifier:@"myCell"];

    // 在tableView: cellForRowAtIndexPath:方法里实例化一个MyTableViewCell,并返回

    MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myCell" forIndexPath:indexPath];

    return cell;

    我们可以重写一下cell,来验证是否是使用自定义cell。因为cell继承自UIView,因此我们可以加入如下方法:

    - (void)drawRect:(CGRect)rect {

        self.backgroundColor = [UIColor redColor];

    }


    3. 自定义cell之XIB

    和纯代码创建类似,只需要在创建的时候勾选创建XIB文件:

    然后我们就会看到XIB里面cell已经自动关联到类MyXibTableViewCell了:

    当然,如果我们创建的时候不勾选Also create XIB file选项,也是可以后续单独创建XIB的,只是创建好的XIB要手动关联到我们自定义cell的类名。

    接下来,我们为XIB的cell起一个标识符:

    最后,我们在注册cell的时候,使用这个标识符找到XIB中的cell,从而创建使用。

    // 注册一个cell

    [_tableView registerNib:[UINib nibWithNibName:@"MyXibTableViewCell" bundle:nil] forCellReuseIdentifier:@"MyXibTableViewCell"];

    在tableView: cellForRowAtIndexPath:方法里实例化一个MyXibTableViewCell,并返回:

    // 返回MyXibTableViewCell

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

        // 实例化一个MyXibTableViewCell

        MyXibTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyXibTableViewCell"];

        // 设置cell相关属性

        // 返回cell

        return cell;

    }

    为了验证是否使用XIB的cell,设置cell的背景颜色:


    4. 自定义cell之storyboard

    先在Main.storyboard里面创建一个tableview和一个cell:

    设置tableview的dalegate和datasource:

    设置类别:

    设置id:

    最后,我们可以省掉注册cell这一步(因为系统会自动帮我们注册),直接在tableView: cellForRowAtIndexPath:方法里实返回MyStoryboardTableViewCell

    // cell

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

        // 实例化一个MyStoryboardTableViewCell

        MyStoryboardTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyStoryboardTableViewCell"];

        // 设置cell相关属性

        if (indexPath.row < self.digitalArr.count) {

            cell.textLabel.text = self.digitalArr[indexPath.row];

        }

        // 返回cell

        return cell;

    }

    设置storyboard里cell的背景颜色:


    五、自定义Header/Footer

    如同自定义cell,上面的代码中返回cell的时候返回我们自己创建的cell就好了。对于Header/Footer也一样,我们首先要分别实现两个代理方法:一个返回高度一个返回内容(UIView类型)。


    // Header高度

    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {

        return 100;

    }

    // Footer高度

    - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {

        return 30;

    }

    // 自定义Header

    - (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, TABLE_WIDTH, 100)];

        UIImageView *imageView = [[UIImageView alloc] initWithFrame:view.bounds];

        imageView.image = [UIImage imageNamed:@"mas091"];

        [view addSubview:imageView];

        return view;

    }

    // 自定义Footer

    - (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {

        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, TABLE_WIDTH, 30)];

        view.backgroundColor = [UIColor blackColor];

        return view;

    }

               

           

     

       

             


    相关文章

      网友评论

          本文标题:iOS开发之UI篇(5)—— UITableView

          本文链接:https://www.haomeiwen.com/subject/owycqctx.html