一、概述
UITbableView作为列表展示信息,除了展示的功能,有时还会用到删除,比如购物车
、收藏列表
等。
- 单行删除:可以直接使用系统自带的删除功能,当横向轻扫cell时,右侧出现红色的删除按钮,点击删除当前cell。或者让表格进入编辑状态后,点击左侧的红色按钮,右侧出现删除按钮,即可删除当前cell。可参照:iOS UITableView删除功能
- 多选删除:点击编辑按钮,让表格进入编辑状态后,每行的左侧出现一个小圆圈,当点击行的时候,可以选中该行或者取消选中该行,当点击删除按钮的时候才会把选中的行全部删除掉。
二、效果图
三、技术分析
- 让tableView进入编辑状态,即
tableView.editing = YES
。
// 取消
[self.tableView setEditing:YES animated:NO];
- 返回编辑模式,即实现
UITableViewDelegate
中的- tableview:editingStyleForRowAtIndexPath:
方法,在里面返回多选模式即UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert
。如果不实现,默认返回的就是删除模式即UITableViewCellEditingStyleDelete
。
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView.isEditing) {
// 多选
return UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert;
}else{
// 删除
return UITableViewCellEditingStyleDelete;
}
}
- 返回编辑模式,即实现
UITableViewDelegate
中的- tableview:editingStyleForRowAtIndexPath:
方法,在里面返回多选模式即UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert
。如果不实现,默认返回的就是删除模式即UITableViewCellEditingStyleDelete
。
// 选中
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView.isEditing) {
NSIndexPath *indexPathM = self.dataSource[indexPath.row];
if (![self.selectedDatas containsObject:indexPathM]) {
[self.selectedDatas addObject:indexPathM];
}
[self _indexPathsForSelectedRowsCountDidChange:tableView.indexPathsForSelectedRows];
return;
}
MHOperationController *operation = [[MHOperationController alloc] init];
NSIndexPath *indexP = self.dataSource[indexPath.row];
operation.title = [NSString stringWithFormat:@"仙剑奇侠传 第%zd集",indexP.row];
[self.navigationController pushViewController:operation animated:YES];
}
// 取消选中
- (void) tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView.isEditing)
{
NSIndexPath *indexPathM = self.dataSource[indexPath.row];
if ([self.selectedDatas containsObject:indexPathM]) {
[self.selectedDatas removeObject:indexPathM];
}
[self _indexPathsForSelectedRowsCountDidChange:tableView.indexPathsForSelectedRows];
}
}
- 点击删除按钮,删除数据源的数据和tableView中所对应的cell。
// delete收藏视频
- (void)_deleteSelectIndexPaths:(NSArray *)indexPaths
{
// 删除数据源
[self.dataSource removeObjectsInArray:self.selectedDatas];
[self.selectedDatas removeAllObjects];
// 删除选中项
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
// 验证数据源
[self _indexPathsForSelectedRowsCountDidChange:self.tableView.indexPathsForSelectedRows];
// 验证没有数据的情况 没有数据 右侧按钮 不能点击 细节处理
if (self.dataSource.count == 0)
{
//没有收藏数据
if(self.rightBarButtonItem.selected)
{
// 编辑状态 -- 取消编辑状态
[self _rightBarButtonItemDidClicked:self.rightBarButtonItem];
}
self.rightBarButtonItem.enabled = NO;
}
}
四、细节处理
- 侧滑状态下点击
编辑
按钮的bug。
// 编辑按钮的点击事件
- (void)_rightBarButtonItemDidClicked:(UIButton *)sender
{
sender.selected = !sender.isSelected;
if (sender.isSelected) {
// 这个是fix掉:当你左滑删除的时候,再点击右上角编辑按钮, cell上的删除按钮不会消失掉的bug。且必须放在 设置tableView.editing = YES;的前面。
[self.tableView reloadData];
// 取消
[self.tableView setEditing:YES animated:NO];
// 全选
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.leftBarButtonItem];
self.leftBarButtonItem.selected = NO;
// show
[self _showDeleteButton];
}else{
// 清空选中栏
[self.selectedDatas removeAllObjects];
// 编辑
[self.tableView setEditing:NO animated:NO];
// 返回
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.backBarButtonItem];
// hide
[self _hideDeleteButton];
}
}
五、拓展
-
如果客户想用系统自带的多选功能,但是要改变选中按钮的样式。例如下图所示:
-
由于该选中图片在界面上已经显示出来了,那肯定是存在的。可以调用 [cell subviews]方法寻找。猜想可能是UIControl的子类。打印Cell的子控件如下图所示: 由下图不难看出,此时选中的cell中有四个子视图类名分别为:
UIView
,UITableViewCellContentView
,_UITableViewSeparatorView
,UITableViewCellEditControl
。UITableViewCellEditControl
肯定是我们想要的,正好里面还有一个imageView
属性。但是查询UITableViewCell
的关于UITableViewCellEditControl
的API,并未发现关于其的解释,则利用KVC(key-value-coding
)键值编码,对OC这门动态语言量身定制,利用runtime运行时原理动态为对象设置属性。
修改选中按钮的样式
/** 修改选中按钮的样式 */
- (void)_changeCellSelectedImage
{
// 利用KVC 设置color
for (UIView *view in self.subviews) {
if ([view isKindOfClass:[UIControl class]])
{
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UIImageView class]])
{
// MHGlobalOrangeTextColor :浅橙色
[subview setValue:MHGlobalOrangeTextColor forKey:@"tintColor"];
}
}
}
}
}
- 由于编辑状态下点击
Cell
,就得调用- (void)_changeCellSelectedImage
这个方法修改选中按钮的样式。以及长按Cell
也得修改样式,根据MVC
设计模式的原则,自定义Cell
,监听Cell
的选中状态
和高亮状态
,从而将业务逻辑屏蔽起来。
/** 选中cell的时候调用 */
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// 非编辑状态下 直接退出
if (!self.isEditing) return;
// 非修改系统选中图标样式
if (!self.isModifySelectionStyle) return;
// 修改系统选择按钮的样式
if (selected) {
// 改变
[self _changeCellSelectedImage];
}
}
/** 长按cell高亮状态下 */
- (void) setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
// 非编辑状态下 直接退出
if (!self.isEditing) return;
// 非修改系统选中图标样式
if (!self.isModifySelectionStyle) return;
if (highlighted) {
// 改变
[self _changeCellSelectedImage];
}
}
- 编辑的情况下,Cell的
选中状态下
和高亮状态
都会调用- (void)_changeCellSelectedImage
这个方法,但该方法内部是在遍历Cell
的子控件,若选中过于频繁,则会占用一点内存(注:Cell
的子控件只有四个,其实影响不大)。则需要利用缓存机制了。Demo中未做缓存处理。
五、期待
- 文章若对您有点帮助,请给个喜欢💗;若没啥帮助,请给点建议。
- 针对文章所述,您阅读有什么疑问;或使用Demo中有什么bug。请在文章底部评论,我会尽快解决问题。
- GitHub地址:https://github.com/CoderMikeHe