iOS UITableView控件

1. UITableView

UITableView继承自UIScrollView,可以用来展示一组或多组内容样式相似的数据。UITableView可以说是iOS开发中最重要的控件之一。

常见属性

// 样式,有UITableViewStylePlain(普通样式)和UITableViewStyleGrouped(分组样式) 
@property (nonatomic, readonly) UITableViewStyle style;
// 数据源
@property (nonatomic, weak, nullable) id <UITableViewDataSource> dataSource;
// 界面代理
@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;

// 每行高度,默认为44
@property (nonatomic) CGFloat rowHeight;
// 每组头部的高度
@property (nonatomic) CGFloat sectionHeaderHeight;
// 每组尾部的高度
@property (nonatomic) CGFloat sectionFooterHeight;
// 估算的行高
@property (nonatomic) CGFloat estimatedRowHeight;
// 估算的每组头部的高度,设置可以优化性能
@property (nonatomic) CGFloat estimatedSectionHeaderHeight;
// 估算的每组尾部的高度
@property (nonatomic) CGFloat estimatedSectionFooterHeight;

// 组数
@property (nonatomic, readonly) NSInteger numberOfSections;
// 选中行的indexPath
@property (nonatomic, readonly, nullable) NSIndexPath *indexPathForSelectedRow;

// 索引条文字的颜色
@property (nonatomic, strong, nullable) UIColor *sectionIndexColor;
// 索引条的背景色
@property (nonatomic, strong, nullable) UIColor *sectionIndexBackgroundColor;

// 分隔线的样式,有UITableViewCellSeparatorStyleNone(没有分割线)和UITableViewCellSeparatorStyleSingleLine(正常分隔线)
@property (nonatomic) UITableViewCellSeparatorStyle separatorStyle;
// 分隔线的颜色
@property (nonatomic, strong, nullable) UIColor *separatorColor;
// 分割线的位置偏移
@property (nonatomic) UIEdgeInsets separatorInset;

// 头部控件,只能设置高度,宽度默认填充表格
@property (nonatomic, strong, nullable) UIView *tableHeaderView;
// 尾部控件,只能设置高度,宽度默认填充表格
@property (nonatomic, strong, nullable) UIView *tableFooterView;

// 是否是编辑模式,默认是NO
@property (nonatomic, getter=isEditing) BOOL editing;
// 处在编辑模式的时候是否允许选中行,默认是NO
@property (nonatomic) BOOL allowsSelectionDuringEditing;
// 处在编辑模式的时候是否允许选中多行数据,默认是NO
@property (nonatomic) BOOL allowsMultipleSelectionDuringEditing

// 获取所有可见的cell
@property (nonatomic, readonly) NSArray<UITableViewCell *> *visibleCells;
// 获取所有可见行的位置信息
@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForVisibleRows;

常见方法

// 全局刷新
- (void)reloadData;
// 刷新索引条
- (void)reloadSectionIndexTitles;

// 返回第section组有多少行
- (NSInteger)numberOfRowsInSection:(NSInteger)section;

// 返回indexPath对应的那一行的cell 
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath;

// 返回第section组的头部view
- (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section;
// 返回第section组的尾部View
- (UITableViewHeaderFooterView *)footerViewForSection:(NSInteger)section;

// 插入数据,刷新数据
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;

// 删除数据
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

// 刷新某一行的数据
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

// 动画开启/关闭编辑模式
- (void)setEditing:(BOOL)editing animated:(BOOL)animated;

// 返回一个带有复用标识的cell
- (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
// 代码自定义cell时注册自定义的cell
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier;

// 滚动到指定位置
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;

// 开始块标志
- (void)beginUpdates;
// 结束快标志
- (void)endUpdates;

动画效果UITableViewRowAnimation

typedef NS_ENUM(NSInteger, UITableViewRowAnimation) {
    UITableViewRowAnimationFade,            // 淡入淡出
    UITableViewRowAnimationRight,           // 向右滑动
    UITableViewRowAnimationLeft,            // 向左滑动
    UITableViewRowAnimationTop,             // 向上滑动
    UITableViewRowAnimationBottom,          // 向下滑动
    UITableViewRowAnimationNone,            // 无动画效果,iOS 3.0以后可用
    UITableViewRowAnimationMiddle,          // 保持cell的中间,iOS 3.2以后可用
    UITableViewRowAnimationAutomatic = 100  // 自动选择一个合适的动画效果
};

UITableViewScrollPosition是滚动的位置

typedef NS_ENUM(NSInteger, UITableViewScrollPosition) {
    UITableViewScrollPositionNone,   // 同UITableViewScrollPositionTop
    UITableViewScrollPositionTop,    // 定位完成后,将定位的行显示在tableView的顶部
    UITableViewScrollPositionMiddle, // 定位完成后,将定位的行显示在tableView的中间
    UITableViewScrollPositionBottom  // 定位完成后,将定位的行显示在tableView最下面
};

2. UITableViewDataSource数据源

UITableViewDataSource中的常用方法

// 设置第section组有多少行数据
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

// 设置每一行cell的内容,cell会被复用
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

// 总共有多少组,默认是一组
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;

// 第section组的头部标题
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
// 第section组的尾部标题
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;

// 设置索引条
- (nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView;

// 是否可以编辑行
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;
// 是否可以移动行 
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;
// 根据editingStyle处理是删除还是添加操作,完成删除、插入操作刷新表格
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;

// 移动cell
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;

3. UITableViewDelegate代理

// 每行cell的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
// 第section组的头部高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
// 第section组的尾部高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;

// 第section组的headerView
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;
// 第section组的footerView
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;

// 选中了某一行cell时调用
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
// 取消选中某一行cell时调用
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath

// 设置表格编辑模式,不实现默认都是删除
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;

// 设置左滑删除按钮的文字
- (nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
// 创建一个左滑出现按钮的操作(UITableViewRowAction)数组
- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath;

// 根据editingStyle处理是删除还是添加操作,完成删除、插入操作刷新表格
 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;

// 移动cell
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;

4. UITableView样式

UITableView的两种样式UITableViewStylePlainUITableViewStyleGrouped

4.1 UITableViewStylePlain样式

  • section(分组) 之间默认是没有间距的。
  • 表格滚动时组头与组尾会自动停留,组头和组尾具有悬停效果。

示例代码

@interface UITableViewPlainViewController ()<UITableViewDataSource, UITableViewDelegate>

@property(nonatomic, strong) UITableView *tableView;
@property(nonatomic, strong) NSMutableArray *data;

@end

@implementation UITableViewPlainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.data = [[NSMutableArray alloc] init];
    for (int index = 8; index < 15; index++) {
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
        [dict setObject:[NSString stringWithFormat:@"title - %d", index] forKey:@"title"];
        
        NSMutableArray *value = [[NSMutableArray alloc] init];
        for (int i = index; i < index*2; i++) {
            [value addObject: [NSString stringWithFormat:@"%d - %d", index, i]];
        }
        [dict setObject:value forKey:@"value"];
        
        [self.data addObject:dict];
    }
    
    [self.view addSubview:self.tableView];
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.bottom.equalTo(self.view);
        make.top.equalTo(self.view).offset(50);
    }];
}

- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
        
        _tableView.delegate = self;
        _tableView.dataSource = self;
        
        // 索引条文字颜色和背景
        _tableView.sectionIndexColor = [UIColor blackColor];
        _tableView.sectionIndexBackgroundColor = [UIColor grayColor];

        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"CellId"];
    }
    return _tableView;
}

#pragma mark - UITableViewDataSource -
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [self.data count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSDictionary *dict = self.data[section];
    NSArray *array = [dict objectForKey:@"value"];
    return [array count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    NSDictionary *dict = self.data[section];
    return [dict objectForKey:@"title"];
}

- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return @[@"8", @"9", @"10", @"11", @"12", @"13", @"14"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellId" forIndexPath:indexPath];

    NSDictionary *dict = self.data[indexPath.section];
    NSArray *array = [dict objectForKey:@"value"];
    cell.textLabel.text = [array objectAtIndex:indexPath.row];

    return cell;
}

@end

显示如下
iOS UITableView控件_第1张图片

取消组头悬停效果

// 去掉UItableview headerview黏性(sticky)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat sectionHeaderHeight = 30;
    if (scrollView.contentOffset.y <= sectionHeaderHeight && scrollView.contentOffset.y >= 0) {
        scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
    } else if (scrollView.contentOffset.y >= sectionHeaderHeight) {
        scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
    }
}

4.2 UITableViewStyleGrouped样式

  • section(分组)之间默认是有间距的。
  • 表格滚动的同时组头与组尾会随之滚动、不停留,组头和组尾不具有悬停效果。

示例代码

@interface UITableViewGroupedViewController ()<UITableViewDataSource, UITableViewDelegate>

@property(nonatomic, strong) UITableView *tableView;
@property(nonatomic, strong) NSMutableArray *data;

@end

@implementation UITableViewGroupedViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.data = [[NSMutableArray alloc] init];
    for (int index = 8; index < 15; index++) {
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
        [dict setObject:[NSString stringWithFormat:@"title - %d", index] forKey:@"title"];
        
        NSMutableArray *value = [[NSMutableArray alloc] init];
        for (int i = index; i < index*2; i++) {
            [value addObject: [NSString stringWithFormat:@"%d - %d", index, i]];
        }
        [dict setObject:value forKey:@"value"];
        
        [self.data addObject:dict];
    }
    
    [self.view addSubview:self.tableView];
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.bottom.equalTo(self.view);
        make.top.equalTo(self.view).offset(50);
    }];
}

- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
        
        _tableView.delegate = self;
        _tableView.dataSource = self;

        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"CellId"];
    }
    return _tableView;
}

#pragma mark - UITableViewDataSource -
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [self.data count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSDictionary *dict = self.data[section];
    NSArray *array = [dict objectForKey:@"value"];
    return [array count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    NSDictionary *dict = self.data[section];
    return [dict objectForKey:@"title"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellId" forIndexPath:indexPath];

    NSDictionary *dict = self.data[indexPath.section];
    NSArray *array = [dict objectForKey:@"value"];
    cell.textLabel.text = [array objectAtIndex:indexPath.row];

    return cell;
}

@end

显示如下,表格的头部和尾部,组间都有默认的间隔
iOS UITableView控件_第2张图片iOS UITableView控件_第3张图片

去除表头、表位和组间的间隔

// 方法一,设置tableView的属性
_tableView.sectionHeaderHeight = 30;
_tableView.sectionFooterHeight = 0;

_tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, CGFLOAT_MIN)];
_tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, CGFLOAT_MIN)];

// 方法二,重写UITableViewDelegate的方法
#pragma mark - UITableViewDelegate -
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 30;
}

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

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    return [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, CGFLOAT_MIN)];
}

5. UITableViewCell

主要属性

// 内容视图,任何cell的子视图都应该添加在这个上面
@property (nonatomic, readonly, strong) UIView *contentView;

// cell的辅助按钮样式
@property (nonatomic) UITableViewCellAccessoryType accessoryType;
// 自定义辅助按钮样式
@property (nonatomic, strong, nullable) UIView *accessoryView;

// 自带格式下的图标
@property (nonatomic, readonly, strong, nullable) UIImageView *imageView;
// 自带格式下的主标题
@property (nonatomic, readonly, strong, nullable) UILabel *textLabel;
// 自带格式下的副标题
@property (nonatomic, readonly, strong, nullable) UILabel *detailTextLabel;

// 选中时的颜色设置,默认是UITableViewCellSelectionStyleDefault,UITableViewCellSelectionStyleNone是无色
@property (nonatomic) UITableViewCellSelectionStyle selectionStyle;
// 是否选中
@property (nonatomic, getter=isSelected) BOOL selected;
// 是否高亮
@property (nonatomic, getter=isHighlighted) BOOL highlighted;

// 背景色
@property (nonatomic, strong, nullable) UIView *backgroundView;
// 选中时背景色
@property (nonatomic, strong, nullable) UIView *selectedBackgroundView;

UITableViewCellStyleUITableViewCell自带的格式

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
    UITableViewCellStyleDefault,	// 默认样式,只有图标和主标题
    UITableViewCellStyleValue1,		// 有主标题和副标题,主标题左对齐,副标题右对齐,可以有图标
    UITableViewCellStyleValue2,		// 有主标题和副标题,主标题和副标题居中对齐,无图标
    UITableViewCellStyleSubtitle	// 有图标、主标题和副标题,副标题在主标题下面
};

显示如下
iOS UITableView控件_第4张图片

UITableViewCellAccessoryType是辅助按钮样式

typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) {
    UITableViewCellAccessoryNone,                   // 无样式
    UITableViewCellAccessoryDisclosureIndicator,    // 右边有一个小箭头
    UITableViewCellAccessoryDetailDisclosureButton, // 右边有一个info按钮和小箭头
    UITableViewCellAccessoryCheckmark,              // 右边有一个打勾
    UITableViewCellAccessoryDetailButton            // 右边有一个info按钮
};

显示如下
iOS UITableView控件_第5张图片

6. UITableView编辑状态

设置UITableViewediting属性或者调用setEditing:(BOOL) animated:(BOOL)方法,都可以让UITableView进入编辑状态。默认编辑状态下都是删除操作。
iOS UITableView控件_第6张图片

需要重写UITableViewDelegatecommitEditingStyle方法,来实现删除操作。

#pragma mark - UITableViewDelegate -
// 自定义删除按钮
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
    return @"删个除";
}

// 定义编辑操作
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [tableView beginUpdates];

        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];
        [self.data removeObjectAtIndex:indexPath.row];
        
        [tableView endUpdates];
    }
}

编辑状态下,点击左侧按钮,左滑出现删除按钮,点击执行删除操作。
iOS UITableView控件_第7张图片

非编辑状态下,可以左滑出现删除按钮,点击执行删除操作。也可以继续左滑删除。
iOS UITableView控件_第8张图片

可以对行的状态进行单独设置,默认的有UITableViewCellEditingStyleDeleteUITableViewCellEditingStyleInsert

// 指定哪些行可以进行编辑
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row % 2 == 0) {
        return true;
    } else {
        return false;
    }
}

#pragma mark - UITableViewDelegate -
// 指定行的编辑状态
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row % 4 == 0) {
        return UITableViewCellEditingStyleDelete;
    } else {
        return UITableViewCellEditingStyleInsert;
    }
}

// 定义编辑操作
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [tableView beginUpdates];

        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];
        [self.data removeObjectAtIndex:indexPath.row];
        
        [tableView endUpdates];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        [tableView beginUpdates];

        NSString* text = [NSString stringWithFormat:@"data%ld", [self.data count]];
        NSIndexPath *insertIndexPath = [NSIndexPath indexPathForRow:(indexPath.row+1) inSection:indexPath.section];
        
        [self.data insertObject:text atIndex:insertIndexPath.row];

        [tableView insertRowsAtIndexPaths:@[insertIndexPath] withRowAnimation:UITableViewRowAnimationTop];
        [tableView endUpdates];
    }
}

显示如下
iOS UITableView控件_第9张图片

自定义表格行操作UITableViewRowAction

+ (instancetype)rowActionWithStyle:(UITableViewRowActionStyle)style title:(nullable NSString *)title
        handler:(void (^)(UITableViewRowAction *action, NSIndexPath *indexPath))handler;

UITableViewRowActionStyle按钮样式,UITableViewRowActionStyleDefault红色背景,UITableViewRowActionStyleNormal灰色背景

typedef NS_ENUM(NSInteger, UITableViewRowActionStyle) {
    UITableViewRowActionStyleDefault = 0,
    UITableViewRowActionStyleDestructive = UITableViewRowActionStyleDefault,
    UITableViewRowActionStyleNormal
}

示例代码,

#pragma mark - UITableViewDelegate -
// 设置自定义按钮
- (NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewRowAction *deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive
            title:@"删除" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
        [tableView beginUpdates];

        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];
        [self.data removeObjectAtIndex:indexPath.row];
        [self.remark removeObjectAtIndex:indexPath.row];
        
        [tableView endUpdates];
    }];
    
    UITableViewRowAction *remarkAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal
            title:@"关注" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
        [self.remark replaceObjectAtIndex:indexPath.row withObject:@"remark"];
        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }];
    
    UITableViewRowAction *addAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal
            title:@"添加" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
        [tableView beginUpdates];

        NSString* text = [NSString stringWithFormat:@"data%ld", [self.data count]];
        NSIndexPath *insertIndexPath = [NSIndexPath indexPathForRow:(indexPath.row+1) inSection:indexPath.section];
        
        [self.data insertObject:text atIndex:insertIndexPath.row];

        [tableView insertRowsAtIndexPaths:@[insertIndexPath] withRowAnimation:UITableViewRowAnimationTop];
        [tableView endUpdates];
    }];
    addAction.backgroundColor = [UIColor blueColor];
    
    // 按钮顺序从右往左排列,左滑到底操作默认调用第一个按钮
    return @[deleteAction, remarkAction, addAction];
}

显示如下
iOS UITableView控件_第10张图片

7. UITableView移动操作

要使UITableView能够移动,首先要进入编辑状态

self.tableView.editing = YES;

进入编辑状态后,左边默认是删除操作。
iOS UITableView控件_第11张图片
重写UITableViewDelegateeditingStyleForRowAtIndexPath方法,返回UITableViewCellEditingStyleNone
iOS UITableView控件_第12张图片
把删除操作去除了,但左边空出来了,重写UITableViewDelegateshouldIndentWhileEditingRowAtIndexPath方法,去掉缩进。
iOS UITableView控件_第13张图片

示例代码

#pragma mark - UITableViewDataSource -
// 行是否可以移动
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    return true;
}

// 移动某个单元格到指定位置
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
    [self.data exchangeObjectAtIndex:sourceIndexPath.row withObjectAtIndex:destinationIndexPath.row];
    [self.tableView moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath];
}

#pragma mark - UITableViewDelegate -
//返回编辑样式,默认的是Delete, None什么都没有
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewCellEditingStyleNone;
}

// 处于编辑状态时,是否有缩进效果, 默认是YES
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}

你可能感兴趣的:(iOS,控件,ios,UITableView)