欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS之UITableView

程序员文章站 2022-06-01 11:18:22
...

  UITableView在app中的应用十分广泛,接下来我们就来简单学习一下UITableView。

一、基本认识

 UITableView继承于UIScrollView,只不过前者只能纵向滑动。UITableView在父类的基础添加了一些属性:

iOS之UITableView

(这些属性是没有遵守代理时的UITableView的本身的属性),这些属性在后面会介绍。

然后再来看看tableview的结构:

iOS之UITableViewiOS之UITableView

二、创建一个UITableView

首先我们在控制器的viewdidload方法中,创建一个UITableView(一般是将这个UITableView作为属性变量)。

//viewDidLoad中
self.tableView = [[UITableView alloc]
initWithFrame:CGRectMake(0, 64, self.view.bounds.size.width, self.view.bounds.size.height) style:
UITableViewStyleGrouped
                      ];

 需要指出的是UITableView的style属性有两个:UITableViewStyleGrouped,UITableViewStylePlain。二者之间的区别:二者的区别

然后我们对这个UITableView本身的属性进行设置:

//ViewDidLoad中
    //分割线颜色
    self.tableView.separatorColor = [UIColor blackColor];
    //分割线类型
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
     //自适应的row的高度
    self.tableView.estimatedRowHeight = 120;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    //背景颜色
    self.tableView.backgroundColor = [UIColor whiteColor];
     self.tableView.delegate = self;
    //这个代理实际上是控制数据源的方法
    self.tableView.dataSource = self;
    [self.view addSubview:self.tableView];

分割线的颜色不用多说,是UIColor类型的;分割线控制的是每一个段中每一个row的分割线的类型,它的类型有三种:

typedef NS_ENUM(NSInteger, UITableViewCellSeparatorStyle) {
    UITableViewCellSeparatorStyleNone,
    UITableViewCellSeparatorStyleSingleLine,
    UITableViewCellSeparatorStyleSingleLineEtched 
NS_ENUM_DEPRECATED_IOS(2_0, 11_0, 
"Use UITableViewCellSeparatorStyleSingleLine for a single line separator.")
}

第一种类型没有分割线,第二种类型有分割线,第三种类型在xcode9.4.1中已经没有使用了,使用第二种来代替。

第一种类型效果

iOS之UITableView

第二种类型效果

 iOS之UITableView

然后是tableview中每个section中的row的高度 ,由于有时每个row中的内容是变化的,所以row的高度需要自适应内容的高度

首先我们要估计一个row的高度,就是estimatedRowHeight,然后再设置实际上的rowheight。

背景颜色不用多说,然后后面的两个都是tableView代理属性,接下来会讲到。

三、代理方法

tableview应该至少有两个代理一个是数据源的代理:UITableViewDataSource,另一个是事件处理的代理:UITableViewDelegate。

1、数据源代理及方法实现

在实现数据源的方法之前我们需要有自己的数据,因此建立一个模型SectionGroup,用来配置每个section和每个row中的数据

//sectionGroup.h
#import <Foundation/Foundation.h>

@interface SectionGroup : NSObject
//每个段的头部
@property (nonatomic,copy)NSString * sectionHeader;
//每个段的尾部
@property (nonatomic,copy)NSString * sectionFooter;
//一般出现在右边作为每个section的快速访问
@property (nonatomic,copy)NSString * sectionIndex;
//设置一个装row的数组
@property (nonatomic,strong)NSMutableArray * rowsArray;

+(instancetype)sectionGroup;
@end


//sectionGroup.m
#import "SectionGroup .h"

@implementation SectionGroup
+(instancetype)sectionGroup{
    SectionGroup * section = [[SectionGroup alloc]init];
    section.sectionHeader = @"这是组的头部";
    section.sectionFooter = @"这是组的底部";
    section.sectionIndex = [NSString stringWithFormat:@"%d",arc4random_uniform(10)];
    section.rowsArray = [NSMutableArray array];
    for (int i = 0 ; i < 10; i++) {
        //每个section中有10个row
        NSString * number = [NSString stringWithFormat:@"这是第%d行",i+1];
        [section.rowsArray addObject:number];
    }
    return section;
}
@end

然后我们在viewcontroller中定义一个数组datalist用来装需要配置的数据,接着我们重写datalist的get方法,因为在点语法点到datalist时,我们需要给相应的对象配置数据。

//懒加载自己的数据
-(NSMutableArray *)dataList{
    if (_dataList == nil) {
        _dataList = [NSMutableArray array];
        for (int i = 0; i<4; i++) {
            //datalist里面装section
            //一共有4个section
            SectionGroup * section = [SectionGroup sectionGroup];
            [_dataList addObject:section];
        }
    }
    return _dataList;
}

这样一来,我们就实现了一个类似二维数组的datalist,首先datalist中装的是一个个的section,每一个section中又装的是一个个的row(一个section中是一个rowArray数组),这样一来我们就实现了文章开头那个tableview结构图的数据形式。

然后我们开始着手将这些数据形式通过UI展示出来,首先我们事先数据源代理的两个方法:numberOfSectionsInTableView: 和 numberOfRowsInSection:


-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    //返回datalist中对象数量,实际就是section的数量
    return self.dataList.count;
}

- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    //由于无法在当前类中访问section中的rowsArray,所以我们将每一个section取出来
    SectionGroup * sections = [self.dataList objectAtIndex:section];
    return sections.rowsArray.count;
}

这些设置完毕后,就会得到一个除了header和footer还有分割线之外没有其他任何内容的tableview,因此我们需要去设置每个row对应的cell,再这之前我们需哟啊看一下row和cell的结构关系:

iOS之UITableView

从图中我们可以看出每一个row就是一个cell,且每一个cell都是可以定制的,每一个cell主要包括三个部分,左边的imgaview,中间的textLabel,最右边的accessoryview。

现在我们可以设置cell,需要实现的代理的方法是:cellForRowAtIndexPath:

- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
    //使用苹果提供的缓存池机制
    //定义一个静态的cell标识符
    static NSString * cellID = @"cell";
    //首先从缓存池中找标识符为cell的cell
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    //如果没有找到,则创建
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
    }
    //先取出对应组,再取出对应行的信息 相当于一个二维数组
    SectionGroup * sections = [self.dataList objectAtIndex:indexPath.section];
    NSString * numberString = [sections.rowsArray objectAtIndex:indexPath.row];
    //设置cell里面的视图
    int random = arc4random_uniform(8);
    if (random == 2||random == 6) {
        //detail只会在cell的类型是subtitl的时候才会显示
        cell.detailTextLabel.text = @"我家的后面有一个很大的园,相传叫作百草园。现在是早已并屋子一起卖给朱文公的子孙了,连那最末次的相见也已经隔了七八年,其中似乎确凿只有一些野草;但那时却是我的乐园。";
    }else{
        cell.detailTextLabel.text = @"说明性的文字";
    }
    //detailTextLabel是处于textlabel下的一段比较小的文字,一般是对内容的概括
    cell.detailTextLabel.numberOfLines = 0;
    cell.imageView.image = [UIImage imageNamed:@"timg"];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    //将这个数据显示到对应的cell上
    cell.textLabel .text = numberString;
    return cell;
}

 由于这个方法返回的是一个UITableViewCell,所以我们需要创建一个对应的对象然后返回,但是有一个问题,就是当我们滑动tableview使一个row消失,另一个row出现时,新出现的的row实际上新创建的,这样是很浪费内存的,有没有一种方法可以使新出现的row使用已经消失的row,改变的只是它的内容呢?苹果提供了一种缓存池机制可以实现cell的复用。要使用这种机制,我们需要定义一个静态的标识符(Identifier),然后在创建时使用[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]方法为新创建的这个cell指定标识符,之后每一次设置cell都优先从当前这个tableview中去找标识符对应的那个cell,然后改变cell的内容即可。

cell设置完毕后,一个简单的tableview视图就算完成了,接下来我们要处理相应的事件了

2、UITableViewDelegate代理方法实现

1、row的删除

对于row的删除需要实现的方法是:editActionsForRowAtIndexPath:


-(NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewRowAction * deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault 
title:@"删除" 
handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
        //删除就是删除掉datalist中的数据,这样在重新加载时就加载新的数据了
        [self.dataList removeObjectAtIndex:indexPath.row];
        //删除后重新加载表
        [tableView reloadData];
    }];
    NSArray * actions = @[deleteAction];
    return actions;
}

由于返回值是一个 UITableViewRowAction的数组,所以我们需要创建UITableViewRowAction,这里初始化时有一个rowActionWithStyle,

typedef NS_ENUM(NSInteger, UITableViewRowActionStyle) {
    UITableViewRowActionStyleDefault = 0,
    UITableViewRowActionStyleDestructive = UITableViewRowActionStyleDefault,
    UITableViewRowActionStyleNormal
} NS_ENUM_AVAILABLE_IOS(8_0) __TVOS_PROHIBITED;

实际上就只有两种类型,normal类型的背景颜色是灰色的,default的背景颜色是红色的。

然后实际上的操作实在block中完成的,实际操作代码这里不做赘述,但需要注意的是一定要在删除后重新加载数据。

最后将这个action装进一个数组中,返回这个数组就行了。

2、row的点击事件的代理方法实现

这个事件需要实现的代理方法是: didSelectRowAtIndexPath:


-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    NSString * string = [NSString stringWithFormat:@"第%lu段 第%lu行",indexPath.section,indexPath.row];
    //显创建提示框控制器
    UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提示" message:string preferredStyle:UIAlertControllerStyleAlert];//样式是alert说明是提示框在中间弹出来,sheet样式是在下面弹出来
    //然后创建提示框行为,就是选项
    UIAlertAction * sureAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    }];
    UIAlertAction * cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    }];
    //将行为添加到控制器上面
    [alert addAction:sureAction];
    [alert addAction:cancelAction];
    [self presentViewController:alert animated:YES completion:nil];
}

3、header与footer的设置

需要实现的方法是:titleForHeaderInSection: 和 titleForFooterInSection:

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    SectionGroup * sections = [self.dataList objectAtIndex:section];
    return sections.sectionHeader;
}
-(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{
    SectionGroup * sections = [self.dataList objectAtIndex:section];
    return sections.sectionFooter;
}

我们之前模型SectionGroup只是数据的形式,显示到对应footer和对应的header上需要通过代理来实现。实现的思路都是先取出对应的数据,然后返回给代理。

4、sectionindex的设置

需要实现的方法: sectionIndexTitlesForTableView:

-(NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView{
    NSMutableArray * indexArray = [NSMutableArray array];
    for (SectionGroup * sections in self.dataList) {
        NSString * string = sections.sectionIndex;
        [indexArray addObject:string];
    }
    return indexArray;
}

 

相关标签: UITableView