遇到一篇非常好的文章,忍不住要保留下来https://segmentfault.com/a/1190000004192662
UITableView 编辑模式详解
UITableView的相关编辑操作非常全,今天我们来做一个总结。跟编辑相关的属性和接口有如下,我们一个一个分析,我们先认真阅读一下相关头文件,我根据意思大概翻译了一下注释。
属性方法
@property (nonatomic, getter=isEditing) BOOL editing;
// 默认状态是非编辑状态,如果不调用下面接口直接设置,是没有动画的
- (void)setEditing:(BOOL)editing animated:(BOOL)animated;
DataSource
// 当增减按钮按下时,用来处理数据和UI的回调。
// 8.0版本后加入的UITableViewRowAction不在这个回调的控制范围内,UITableViewRowAction有单独的回调Block。
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;
// 这个回调实现了以后,就会出现更换位置的按钮,回调本身用来处理更换位置后的数据交换。
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;
// 这个回调决定了在当前indexPath的Cell是否可以编辑。
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;
// 这个回调决定了在当前indexPath的Cell是否可以移动。
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;
Delegate
// 这个回调很关键,返回Cell的编辑样式。
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
// 删除按钮的文字
- (nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(3_0) __TVOS_PROHIBITED;
// 8.0后侧滑菜单的新接口,支持多个侧滑按钮。
- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0) __TVOS_PROHIBITED;
// 这个接口决定编辑状态下的Cell是否需要缩进。
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath;
// 这是两个状态回调
- (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath __TVOS_PROHIBITED;
- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath __TVOS_PROHIBITED;
编辑状态
UITableView通过editing属性控制编辑状态,调用- (void)setEditing:(BOOL)editing animated:(BOOL)animated接口,可以决定是否使用原生的变换动画。
当调用这个接口,并将editing设为YES是,UITableView将开始询问代理(Delegate)需要编辑哪些Cell,用什么样的方式编辑。
首先调用回调方法- (BOOL)tableView:(UITableView )tableView canEditRowAtIndexPath:(NSIndexPath )indexPath;,这里需要返回YES;
然后依次为各个Cell调用- (UITableViewCellEditingStyle)tableView:(UITableView )tableView editingStyleForRowAtIndexPath:(NSIndexPath )indexPath;方法获取编辑样式。
typedef NS_ENUM(NSInteger, UITableViewCellEditingStyle) {
UITableViewCellEditingStyleNone,
UITableViewCellEditingStyleDelete,
UITableViewCellEditingStyleInsert
};
编辑样式枚举有三种,位运算组合则由不同的用途。
UITableViewCellEditingStyleNone 没有编辑样式
UITableViewCellEditingStyleDelete 删除样式 (左边是红色减号)
UITableViewCellEditingStyleInsert 插入样式 (左边是绿色加号)
UITableViewCellEditingStyleDelete|UITableViewCellEditingStyleInsert 多选模式,左边是蓝色对号
特别注意,右边的移动并不是这里控制的,需要实现下面这个回调才会出现。
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;
另外对于新手来说,要明白这里的回调都没有对UI和数据进行操作,开发者需要在回调中,完成相应的操作。比如删除或者添加一条数据,应在
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;
上面这个回调中,根据editingStyle进行判断,处理对应的UI和数据。
8.0版本后的多选侧滑菜单
8.0版本后,短信等原生应用都有了侧滑多按钮选择,原来是苹果的前端团队为TableView加入相关接口,这里给个例子
- (NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
return @[
[UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:NSLocalizedString(@"编辑", nil) handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
}],
[UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:NSLocalizedString(@"删除", nil) handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
}]
];
}
数据与UI更新
数据更新没什么好说的,直接操作数据容器就好,无论是数组、字典还是CoreData数据。UI更新则需要使用TableView的方法,如果需求reloadData无法满足,则必须使用下面的方法
- (void)beginUpdates; // allow multiple insert/delete of rows and sections to be animated simultaneously. Nestable
- (void)endUpdates; // only call insert/delete/reload calls or change the editing state inside an update block. otherwise things like row count, etc. may be invalid.
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0);
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection NS_AVAILABLE_IOS(5_0);
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0);
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath NS_AVAILABLE_IOS(5_0);
beginUpdates和endUpdates两个方法,在你需要批量处理Cell的时候,用来包裹住你的处理代码,其他方法名字都很直观,不一一介绍了。
最后给大家推荐一个Cocoa框架里的功能强大的类NSFetchedResultsController,用于绑定CoreData数据和UITableView或者UICollectionView,直接封装好所有的UI操作代码,只要数据有变动,UI自动更新,爽的不要不要的,妈妈再也不用担心我的TableView写不好了,下一篇文章我准备详细讲一讲这个有趣的类。
4月22日更新
遇到一个有意思的需求,就是编辑模式下,不让cell缩进,要怎么做呢?
cell.shouldIndentWhileEditing = NO;
或者
注意官方注释
// Controls whether the background is indented while editing. If not implemented, the default is YES. This is unrelated to the indentation level below. This method only applies to grouped style table views.
只对grouped的TableView有效。