iOS之UITableView

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

一、基本认识

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

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

然后再来看看tableview的结构:

二、创建一个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中已经没有使用了,使用第二种来代替。

第一种类型效果

第二种类型效果

 

然后是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的结构关系:

从图中我们可以看出每一个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;
}

 

发布了32 篇原创文章 · 获赞 8 · 访问量 9515
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览