iOS之UITableView略解

作者: helloDolin | 来源:发表于2016-05-12 17:56 被阅读1335次

一.UITableView的重要性

  1. 产品上讲:
    App靠内容黏住用户,没有内容的App,注定是没有生命力的。 而UITableView,便是用于海量数据的展示,我们平时使用的软件中到处都可以看到它的影子,类似于微信、QQ、新浪微博等软件基本上随处都是UITableView

  2. 技术上讲:
    UITableView具有重用和延迟加载等特性, 只要正确使用,海量的数据便可在这张表上,一览无余;
    我们可以借鉴苹果设计UITableView的思想来自定义控件(这点很重要)

二.UITableView的结构

  1. tableView的大致结构


    结构.png
  2. tableViewCell
    cell苹果提供了四种类型

cell苹果提供了四种类型

本例的示意图是用xib自定义的cell

accessory view

accessory view苹果也提供了几种类型


Snip20160511_13.png
Paste_Image.png

三.本人项目中常用的方法(比较乱,请谅解)

1.两种初始化的方式
UITableViewStylePlain,UITableViewStyleGrouped
看看官方文档的解释:


UITableViewStylePlain.png UITableViewStyleGrouped.png
  • 区别一:(翻译一下子)
    UITableViewStylePlain形式的tableView section的headerView和footerView会浮动
    UITableViewStyleGrouped形式的tableView则不会
  • 区别二:这个文字不好描述,还是直接上图吧
UITableViewStylePlain.png UITableViewStyleGrouped.png

UITableViewStyleGrouped形式的tableView,若要修改section之间的距离,需要实现以下四个代理方法:

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

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

}

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

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

2.闭合cell的分割线(相信有很多开发者碰到这样的需求,默认的分割线左边是没有闭合的)
实现以下代理:

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
   
    if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
        [cell setSeparatorInset:UIEdgeInsetsZero];
    }
    
    if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) {
        [cell setPreservesSuperviewLayoutMargins:NO];
    }
    
    if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
        [cell setLayoutMargins:UIEdgeInsetsZero];
    }
}

3.取消cell的分割线

  • 第一种方式
    设置tableView的separatorStyle 为UITableViewCellSeparatorStyleNone
    苹果提供了如下枚举
typedef NS_ENUM(NSInteger, UITableViewCellSeparatorStyle) {
    UITableViewCellSeparatorStyleNone,
    UITableViewCellSeparatorStyleSingleLine,
    UITableViewCellSeparatorStyleSingleLineEtched   // This separator style is only supported for grouped style table views currently
} __TVOS_PROHIBITED;

第三个说是只在grouped style table views才可以用但是和UITableViewCellSeparatorStyleNone的效果是一样的,不知道是不是bug(应该是我太菜,没理解)

  • 第二种方式
    这种方式的好处在于只让某个cell的分割线取消时,比较好处理
[cell setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 1000000)];

4.关于自定义cell

  • 第一种方式:xib拖
  • 第二种方式:纯代码
// xib方式的注册
[_tableView registerNib:[UINib nibWithNibName:@"" bundle:nil] forCellReuseIdentifier:@""];
// 纯代码方式的注册
 [_tableView registerClass:[Cell class] forCellReuseIdentifier:@""];

注意:纯代码的cell布局尽量在这里处理

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

5.cell的selectionStyle
首先看下官方提供的几种:

typedef NS_ENUM(NSInteger, UITableViewCellSelectionStyle) {
    UITableViewCellSelectionStyleNone,
    UITableViewCellSelectionStyleBlue,
    UITableViewCellSelectionStyleGray,
    UITableViewCellSelectionStyleDefault NS_ENUM_AVAILABLE_IOS(7_0)
};

如果我们要自定义选中状态呢?

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
    if (selected) {
        self.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"xozaa_discover_activity"]];
        self.lblText.textColor = [UIColor whiteColor];
    } else {
        self.selectedBackgroundView = nil;
        self.lblText.textColor = [UIColor blackColor];
    }
}

效果如下:


效果图

这里需要注意的就是cell里其他控件的状态改变是否要变化

6.取消选中状态
[tableView deselectRowAtIndexPath:indexPath animated:YES];

如果有这样的需求:跳转到另一个页面再回到本页面,要告诉用户我刚才点了哪个cell,那么我们可以这样处理:

(void) viewWillAppear: (BOOL)inAnimated {
NSIndexPath *selectedIndexPath = [self.table indexpathForSelectedRow];
if(selectedIndexPath)  {
    [self.table deselectRowAtIndexpath:selectedIndexPath animated:NO];
  }
}

7.cell的赋值
tableView的数据处理应该大部分都用数组处理吧,假设以数组处理,如下

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TestTableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"TestTableViewCell"];
    
    NSDictionary* dic = _arr[indexPath.row];
    [cell fillData:dic];

    return cell;
}

这里要说的是给cell填充数据时,我们尽量不要在ViewController里边写,展示的事情应该交给View层去处理,ViewController只是把Model的数据发送过去。

之前见过实现这个代理时写的超长的方法,所有的业务逻辑,展示逻辑都在这里,看这种代码的时候真的很痛苦

减轻ViewController的压力,维护起来也很方便

8.左滑cell处理
实现如下代理,注意这个代理仅支持iOS8以后的系统

- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0) __TVOS_PROHIBITED;

效果图:

效果图.png

直接上代码


#pragma mark -  UITableViewDelegate
/**
 *  左滑事件
 *
 *  @param tableView
 *  @param indexPath
 *
 *  @return 数组
 */
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
    // 删除按钮
    UITableViewRowAction *deleteRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"删除" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath){
        NSLog(@"删除");
        // 注意数据源对应删除,否则会报Assertion failure in -[UITableView _endCellAnimationsWithContext:] 这样的错误
        // 如果一个分组中,有多条数据时,你删除其中一条,正确;当一个分组中,你要删除唯一的一条时,仍然会报出如上的错误!,测试需要删除这个section
        if (indexPath.section == 1) {
            [arr2 removeObjectAtIndex:indexPath.row];
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        }
    }];
    
    // 置顶按钮
    UITableViewRowAction *toTopRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"置顶" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath){
        NSLog(@"置顶");
        // 注意数据源对应处理
        [tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
    }];
    // 设置按钮的背景色
    toTopRowAction.backgroundColor = [UIColor orangeColor];
    
    // 其他按钮
    UITableViewRowAction *otherRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"其他" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath){
        NSLog(@"其他");
    }];
    // 设置按钮的背景色
    otherRowAction.backgroundColor = [UIColor cyanColor];
    
    //返回按钮数组
    return @[deleteRowAction, toTopRowAction, otherRowAction];
}

9.各种reload
假设我们的tableView初始化已经完毕

@property (nonatomic,strong) UITableView *tableView;

// 刷新整个表
[self.tableView reloadData];

// 某个section的刷新
NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:2];
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];

// 某个cell的刷新
NSIndexPath *indexPath=[NSIndexPath indexPathForRow:3 inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

10 UITableView初级优化
如果我们的cell高度是固定的,那么我强烈建议定义cell高度的地方放到初始化的时候
而不要去走代理方法:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
     return 100.0;
}

很多人都把优化的重点放到了 cell for row at indexpath 那个方法里了,在这里尽可能的少计算,但是却忽略了另一个很轻松就能提升加载时间的方法 :

eg:_tableView.rowHeight = 100.0;

Table View 在每次 reload data 时都会要所有 cell 的高度!这就是说你有一百行 cell,就x向代理要100次每个cell 的高度,而不是当前屏幕显示的cell 的数量的高度!减少 计算高度时的时间,对于提升加载 Table View 的速度有非常明显的提高!


暂时先写这么多吧,其实还有很多小细节没提及到(tableView的刷新,section的刷新,cell的刷新,cell的添加、删除,表格右边建立一个浮动的索引,控制该表格滚动到指定indexPath等等)这篇是比较基础的东东,但还是希望会给大家带来帮助 O(∩_∩)O

相关文章

网友评论

本文标题:iOS之UITableView略解

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