关于iOS`UITableViewCell 使用系统 多选 全选问题

  最近做一个项目,要用到UITableView中cell的多选和全选。 

上网简单一搜索,发现很多小伙伴对于这块是自己自定义cell且通过代码判断,再去改变UI来实现。很多方法的理论基础基本上都是自己给cell写一个属性来记录其是否未被选中的状态,然后单独用一个字典来装这些状态和对应cell所在的indexPath  每次加载cell再去做判断 如何显示选中与非选中状态UI不同。 就突然记起Apple对这块功能貌似是已经实现了的,并且留了简单易懂的接口给我们。

于是首先建立一个简单的TableView如下图:


  接下来简单为tableView添加俩行代码就可以实现选中后那个蓝色的勾。

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. table.editing = YES;  
  2. table.allowsMultipleSelectionDuringEditing = YES;  
代码中第一行意思为table进入编辑状态,第二行是允许table在编辑的时候多选。当然关于table的编辑在<UITableViewDelegate>中有关于table编辑的插入 删除等等不在此赘述。 然后接下来是实现cell的全选的实现。此处我是将点击全选和取消全选都写在左边的NavigationItem上:

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. - (void)viewDidLoad {  
  2.     [super viewDidLoad];  
  3.     // Do any additional setup after loading the view, typically from a nib.  
  4.       
  5.     self.title = @"多选cellDemo";  
  6.       
  7.     [self customTableView];  
  8.       
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.     //为左边的item设置标题和动作  
  2.     self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"全选" style:UIBarButtonItemStylePlain target:self action:@selector(selecteAllCells:)];  
  3.       
  4. }  
  5.   
  6.   
  7. #pragma mark Actions  
  8. - (void)selecteAllCells:(UIBarButtonItem *)sender {  
  9.     if (_isAllSelected == NO) {  
  10.           
  11.         _isAllSelected = YES;  
  12.         [sender setTitle:@"取消"];  
  13.           
  14.         for (int i = 0; i < self.dataArray.count; i++) {  
  15.   
  16.             NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];  
  17.             [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionTop];  
  18.         }  
  19.     } else {  
  20.           
  21.         _isAllSelected = NO;  
  22.         [sender setTitle:@"全选"];  
  23.           
  24.         for (int i = 0; i < self.dataArray.count; i++) {  
  25.               
  26.             NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];  
  27.             [self.tableView deselectRowAtIndexPath:indexPath animated:YES];  
  28.         }  
  29.     }  
  30.       
  31. }  

代码实现逻辑主要是讲所有cell的indexPath找出来用table的方法选择和取消选择cell.效果图如下:


选中之后调用[tableView indexPathsForSelectedRows]获得所有indexPath.

此时有新的需求 选中之后背景的灰色不太好看 客户只想要保留蓝色的勾,不要这个背景灰色。那么可在<UITableViewDataSource>的返回和自定义cell的函数中设置 

   cell.selectedBackgroundView = [[UIView allocinit]; 另外一个小tip 可设置tableView的tableFooterView = [[UIView alloc] init]可以消除tableView下面那些多出来的线条。效果图如下:



此时客户需求又有变化,客户觉得那个选中图片圆圈的背景图片蓝色太深 不好看,让改淡些。心里一万匹草泥马呼啸而过 但咱作为有素质有技术的工程师不能表现出来对吧。于是又接着改。直到此时 仍不愿去自定义cell里面的控件写逻辑 忒麻烦了有木有。

于是想,该图片在界面上已经显示出来了,那肯定是存在的于是在调用 [cell subviews]方法寻找,发现在<UITableViewDataSource>的自定义cell那个方法里没什么线索。



换个地方,于是在<UITableViewDelegate>里面选择回调的地方用类似的方法找,果然皇天不负有心人。

由下图可见,此时选中的cell中有四个子视图 类名分别为:UIView,UITableViewCellContentView,UITableViewSeparatorView,UITableViewCellEditControl.猜也能猜到我们的蓝色勾勾在第四个里面,正好里面还有一个imageView属性。然后问题又来了,这个UITableViewCellEditControl是何方神圣?翻遍了dash里面iOS9.0文档和UITableViewCell头文件并没有找到这么一个类。想来可能是一个匿名类,不过还好其基类是UIControl类,于是可以用面向对象三大特性之一的多态,循环判断找出该对象。没有头文件的类,属性自然也没有setter和getter方法,不能通过点语法读取到。于是我们还有一个强大的工具,叫做KVC(key-value-coding)键值编码,对OC这门动态语言量身定制,利用runtime运行时原理动态为对象设置属性。于是通过以下代码设置

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #pragma mark <UITableViewDelegate>  
  2. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {  
  3.       
  4.     NSArray *subviews = [[tableView cellForRowAtIndexPath:indexPath] subviews];  
  5.       
  6.     for (id obj in subviews) {  
  7.         if ([obj isKindOfClass:[UIControl class]]) {  
  8.   
  9.             for (id subview in [obj subviews]) {  
  10.                 if ([subview isKindOfClass:[UIImageView class]]) {  
  11.                       
  12.                     [subview setValue:blueBackground forKey:@"tintColor"];  
  13.                     break;  
  14.                 }  
  15.                   
  16.             }  
  17.         }  
  18.     }  
  19. }  

至此,终于达到目的,且重要的是没用多少代码有木有。效果图如下



最后,还有一个小小Bug,因为我们的全选的做法是循环所有cell的indexPath 然后掉方法

[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionTop];这样,全选的cell颜色又回到了原来那么深 于是将改变cell选中图片背景色的代码封装成方法 在这俩处调用整理代码如图



本篇博客Demo地址https://github.com/xmy0010/DemoForCSDN

本人邮箱18144200589@163.com欢迎小伙伴一起讨论,学习,进步。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
iOS-RATreeView是一个开源的第三方库,提供了多层级的UITableView展示功能。使用该库可以轻松实现多级列表的展开与收起。 首先,在项目中引入iOS-RATreeView库。可以使用CocoaPods引入,也可以手动下载并导入。 接下来,在需要使用多级列表的UIViewController中,创建一个RADataObject类型的数组,用来存储数据。RADataObject是iOS-RATreeView中的一个数据模型,用来表示一条记录。每个RADataObject可以包含多个子RADataObject,从而形成多级列表。 ``` // 创建RADataObject数组 NSMutableArray *data = [NSMutableArray array]; // 创建一级列表 RADataObject *level1_1 = [RADataObject dataObjectWithName:@"Level 1-1" children:nil]; RADataObject *level1_2 = [RADataObject dataObjectWithName:@"Level 1-2" children:nil]; RADataObject *level1_3 = [RADataObject dataObjectWithName:@"Level 1-3" children:nil]; // 创建二级列表 RADataObject *level2_1 = [RADataObject dataObjectWithName:@"Level 2-1" children:nil]; RADataObject *level2_2 = [RADataObject dataObjectWithName:@"Level 2-2" children:nil]; RADataObject *level2_3 = [RADataObject dataObjectWithName:@"Level 2-3" children:nil]; // 将二级列表添加到一级列表中 level1_1.children = @[level2_1, level2_2]; level1_2.children = @[level2_3]; // 将一级列表添加到RADataObject数组中 [data addObject:level1_1]; [data addObject:level1_2]; [data addObject:level1_3]; ``` 创建完数据源后,需要创建RATreeView对象,并设置代理和数据源。 ``` // 创建RATreeView对象 self.treeView = [[RATreeView alloc] initWithFrame:self.view.bounds]; // 设置代理和数据源 self.treeView.delegate = self; self.treeView.dataSource = self; ``` 接下来实现RATreeViewDataSource协议中的方法,用来返回列表的数据。具体实现可以参考下面的代码。 ``` - (UITableViewCell *)treeView:(RATreeView *)treeView cellForItem:(id)item { static NSString *identifier = @"Cell"; UITableViewCell *cell = [treeView dequeueReusableCellWithIdentifier:identifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; } // 获取RADataObject对象 RADataObject *dataObject = item; // 设置cell的文本 cell.textLabel.text = dataObject.name; return cell; } - (NSInteger)treeView:(RATreeView *)treeView numberOfChildrenOfItem:(id)item { if (item == nil) { // 如果item为nil,表示请求根节点的子节点数量 return self.data.count; } else { // 获取RADataObject对象 RADataObject *dataObject = item; // 返回子节点数量 return dataObject.children.count; } } - (id)treeView:(RATreeView *)treeView child:(NSInteger)index ofItem:(id)item { if (item == nil) { // 如果item为nil,表示请求根节点的子节点 return self.data[index]; } else { // 获取RADataObject对象 RADataObject *dataObject = item; // 返回子节点 return dataObject.children[index]; } } - (BOOL)treeView:(RATreeView *)treeView canEditRowForItem:(id)item { // 返回是否可以编辑 return YES; } - (void)treeView:(RATreeView *)treeView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowForItem:(id)item { if (editingStyle == UITableViewCellEditingStyleDelete) { // 删除节点 RADataObject *parentObject = [treeView parentForItem:item]; if (parentObject) { NSMutableArray *children = [NSMutableArray arrayWithArray:parentObject.children]; [children removeObject:item]; parentObject.children = children; } else { NSMutableArray *data = [NSMutableArray arrayWithArray:self.data]; [data removeObject:item]; self.data = data; } // 刷新列表 [treeView reloadData]; } } ``` 最后,在RATreeViewDelegate协议中实现展开与收起节点的方法。 ``` - (void)treeView:(RATreeView *)treeView willExpandRowForItem:(id)item { // 获取RADataObject对象 RADataObject *dataObject = item; // 设置节点的展开状态 dataObject.expanded = YES; } - (void)treeView:(RATreeView *)treeView willCollapseRowForItem:(id)item { // 获取RADataObject对象 RADataObject *dataObject = item; // 设置节点的展开状态 dataObject.expanded = NO; } ``` 至此,多级列表展开与收起的功能就实现了。在需要展示多级列表的地方,只需要将创建的RATreeView添加到视图中即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值