UITableView使用中常见的问题

在介绍UITableView使用中常见的问题之前,我们先简单介绍一下UITableView的相关概念。UITableView是UIKit框架库中的一个很重要的类,它被用来实现竖向排列的表格,例如微信中的消息列表和好友列表等;也被用来实现feed流,例如微博中的动态。

UITableView的Delegate和DataSource

要想使用UItableView,那么数据源和代理是不可或缺的。数据源顾名思义就是为tableView提供数据的对象,它负责告诉UItableVIew对象有几个section,每个section中有几个cell以及每个cell中要显示什么内容等;而UITableView的代理方法的主要功能是在tableView被执行了相应的操作后,对应的代理方法将这一消息传递给使用方(一般情况下是对应的VC),让其来进行一系列的后续动作。 代理方法和数据源方法在语法上没有什么区别,都是使用OC中协议的语法实现的,一个vc要使用tableView就要遵守UITableViewDelegate和UITableViewDataSource这两个协议,因而要实现其中的方法。下面看下具体都有哪些方法。

//UITableViewDelegate常用方法

// Display customization

//一个cell将要被展示时调用
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
//一个section的header将要被展示时调用
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
//一个section的header将要被展示时调用
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
//cell展示完成时调用
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath API_AVAILABLE(ios(6.0));
//header展示完成时调用
- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
//footer展示完成时调用
- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));

// Variable height support

//设置cell的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
//设置header的高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
//设置footer的高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
//UITableViewDataSource常用方法

//一个section中有几行cell
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
//tableView中有几个section
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
//用来设置每行的内容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

cell复用时经常出现的问题

我们知道每台机器的内存空间都是有限的,当我们一个tableView要显示的数据很多的时候,就需要很多条cell来显示,如果我们为每一条数据都创建一个新的cell对象那么将会浪费很多的空间,甚至导致程序崩溃。这时候我们就需要复用cell,屏幕上可以显示的cell个数是固定不变的,当一个新的cell滑进屏幕时必然会有一个之前存在于屏幕中的cell滑出去,那我们就可以复用滑出屏幕的那个cell对象来显示新的数据。

这是cell复用的概念,刚接触iOS编程的同学在进行cell复用时很可能遇到的问题是:之前cell上的内容没有清除掉,新的内容又渲染到了cell中导致一条 cell上的数据重叠。如下图:

可能大多数同学都知道这需要把cell上原来的内容remove掉,也就是把cell的所有子视图都为其调用removeFromSuperView方法,但苦于对语言的不熟练导致心有余而力不足,不知道该选用哪个方法来实现这一功能,下面我们就列举两种方法,清楚某个视图上的所有子视图。

//方法1:makeObjectsPerformSelector
[cell.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];


//方法2:用循环实现
for (UIView *view in cell.subviews) {
     [view removeFromSuperview];
}

UITableView的编辑模式

UITableViewCell的编辑模式有两种:插入和删除。当我们删除一个cell的时候注意要先删除对应的数据而后才能删除cell,否则会导致crash,插入也是相同,首先要插入数据才能添加新的cell。默认情况下UIItableView的编辑模式是关闭的,我们可以实现如下的代理方法实现左滑删除,效果如下:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    NSMutableArray *arr = [[NSMutableArray alloc] initWithArray:self.myArray];
    [arr removeObjectAtIndex:0];
    self.myArray = [arr copy];
    [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}

但是这个代理方法的功能很单一,只能实现如图的效果,我们会发现微信的消息列表左滑一个cell的时候,会出现两个按钮让用户有更多的选择,我们可以通过如下代理方法来实现:


- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UIContextualAction *action = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:@"hahaha" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
        
        
    }];
    
    UIContextualAction *action1 = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:@"delete" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
        NSMutableArray *arr = [[NSMutableArray alloc] initWithArray:self.myArray];
        [arr removeObjectAtIndex:indexPath.row];
        NSLog(@"%ld", indexPath.row);
        self.myArray = [arr copy];
        [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }];
    
    return [UISwipeActionsConfiguration configurationWithActions:@[action, action1]];;
}

这个代理方法中我们实现了两个action,一个“hahaha”,一个“delete”,在初始化action的时候有两种样式:UIContextualActionStyleNormal和UIContextualActionStyleDestructive,这里我们将delete按钮设置为UIContextualActionStyleDestructive。可以看到我们只实现了delete按钮的功能。效果如下图:

如图我们设置了两个按钮,可以根据不同的需要实现不同的逻辑。

 

 

 


版权声明:本文为Mr_H9527原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。