有关UITableViewCell的侧滑删除以及使用相关大神框架MGSwipeTableCell遇到的小问题

  提起笔,却不知道从何写起了,今天一整天都耗费在了这个可能根本不算是问题的小问题上,至今仍有一种蛋蛋的忧桑。。(噢,不是提笔,是键盘手T_T)

  表格视图在项目中就像是每日的家常便饭,在cell上添加侧滑删除功能这种需求也是遍地可见。而就是这么一个家常菜却坑了我一天,可能我是真的闲的蛋疼吧,好吧,其实,讲道理还是我太菜,人艰不拆。

  好了废话不多说,运用系统自带的API实现侧滑删除功能其实非常简单:


//- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
//    
//    if (editingStyle == UITableViewCellEditingStyleDelete) {
//        
//        删除数据源对应模型
//        [self.shops removeObjectAtIndex:indexPath.row];
//        从tableView中删除
//        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationTop];      
//    }
//    
//}
只要重写上述代理方法,实现删除模式的相关处理即可,当然你也可以加一个else语句对于编辑模式不是删除时做一些其他的处理,比如控制台提示,或者是处理其它模式,比如插入insert,
并且上述代码也可以不从tableView中删除,直接从数据源中删除,然后刷新表格就好了[tableView reloadData];这里我个人认为苹果的deleteRowsAtIndexPaths:方法肯定是有它的
好处的,试想,数据很多的话,我们为了删除tableView中的一行,而刷新整个表格,这样真的好吗?
另,上述只是最简单的处理情况,倘若数据很多,本地有数据库存储,在上述基础上还要进行数据库的删除和存储操作,同样的,如果数据源是从服务器获取的,那么还要相关请求删除服务器上
的数据,不然下次来到该界面,删掉的数据又会显示。
//upload-images.jianshu.io/upload_images/1097106-7a043347245166b0.gif?imageMogr2/auto-orient/strip

  从iOS8开始,苹果开放了这样一个API:


- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath

  返回一个UITableViewRowAction数组,每一个"Action"代表一个侧滑删除的Button。这样侧滑每一行Cell可以有更多按钮提供给用户交互。






  不幸地是这个API只在iOS8才有,这样iOS8以下就没办法使用到这种效果。这种情况下我们不得不使用第三方库或者自己重写UITableViewCell来“模拟”出这种效果。那其实呢,我今天所要得需求是最基本的只有删除就OK的,但是我是自定义的cell,然后我遇到一个问题,就是滑动cell,删除按钮很难出现,十次能不能滑出一次还是个问题。

  我项目中自定义的cell如上图,至于为什么会划不出删除按钮,可以参见我上篇博客,翻译国外大神的文章制作一个可以滑动操作的 Table View Cell

  一般情况下只要你重写了- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {}方法,不实现也可以滑出按钮。那对于这样一个简单地需求我们完全可以自定义一个cell去实现它,思路很简单,可以在系统的UITableViewCell基础上添加一个ScrollView,再添加需要的菜单按钮,或者通过在系统的cell上添加滑动手势,监听来实现。我今天浪费了很多时间,其中有一个原因是原本我想自己封装一套的,结果发现要想真正写好一个框架其实并非易事,看似简单地功能需求,你要考虑的问题可能会很多。

  我最后选择了使用第三方框架,  找了几个对比看了一下,https://github.com/MortimerGoro/MGSwipeTableCell 这个算是封装的比较成熟的了,支持多种侧滑方式以及立体等各种效果 。他里面封装了MGSwipeTableCell和MGSwipeButton,你只需要让你的cell继承于MGSwipeTableCell,然后像这样在tableView的数据源方法里面,创建cell的同时,给它配置侧滑菜单需要的buttons数组传给它就行。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * reuseIdentifier = @"programmaticCell";
    MGSwipeTableCell * cell = [self.tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
    if (!cell) {
        cell = [[MGSwipeTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
    }

    cell.textLabel.text = @"Title";
    cell.detailTextLabel.text = @"Detail text";
    cell.delegate = self; //optional


    //configure left buttons
    cell.leftButtons = @[[MGSwipeButton buttonWithTitle:@"" icon:[UIImage imageNamed:@"check.png"] backgroundColor:[UIColor greenColor]],
                          [MGSwipeButton buttonWithTitle:@"" icon:[UIImage imageNamed:@"fav.png"] backgroundColor:[UIColor blueColor]]];
    cell.leftSwipeSettings.transition = MGSwipeTransition3D;

    //configure right buttons
    cell.rightButtons = @[[MGSwipeButton buttonWithTitle:@"Delete" backgroundColor:[UIColor redColor]],
                           [MGSwipeButton buttonWithTitle:@"More" backgroundColor:[UIColor lightGrayColor]]];
    cell.rightSwipeSettings.transition = MGSwipeTransition3D;
    return cell;
}

  监听菜单里面button的点击事件,作者提供了两种选择,一种就是在上述代码中设置cell的代理,实现它的一些代理方法;另一种更方便就MGSwipeButton

里提供了一个block回调,你可以在初始化MGSwipeButton的时候添加它

[MGSwipeButton buttonWithTitle:@"More" backgroundColor:[UIColor lightGrayColor] callback:^BOOL(MGSwipeTableCell *sender) {
      NSLog(@"Convenience callback for swipe buttons!");
}]

  他提供的接口非常完善,具体的可以在MGSwipeTableCell和MGSwipeButton的.h文件里面看。我今天选择的就是使用block,一开始遇到的问题见如下代码,注释

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

        cell.shop = self.shops[indexPath.row];
        cell.myDelegate = self;
   
    cell.rightButtons = @[[MGSwipeButton buttonWithTitle:@"删除" backgroundColor:[UIColor magentaColor] padding:50 callback:^BOOL(MGSwipeTableCell *sender) {
       
        [self.shops removeObjectAtIndex:indexPath.row];
        NSLog(@"%d-------%@",self.shops.count ,self.shops);

// 当我用这个方法删除时,偶尔会崩溃,具体原因不明  测试了很多遍,感觉是删太快就会出现,也可能是快速点击两次删除会出现,总之很奇怪,慢慢地一个一个删一般不会有问题
(总之,用户正常使用的话,不影响,当时也没强行深究原因,暂且记下一笔)
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationTop];

//当我用这个方法删除时,好像没有什么问题,问题出在哪呢?(再后来发现好像也有崩溃
//        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:0]] withRowAnimation:UITableViewRowAnimationNone];

//不用删除方法,直接强行刷新表格,好像没出过bug
//        [tableView reloadData];
        return true;     
    }],
                          ];
    cell.rightSwipeSettings.transition = MGSwipeTransition3D;
        return cell;

}

  因为这是个概率性出现的bug,我这种菜鸟,在那无脑的尝试,瞎捣腾,试了度娘上的各种事实证明并不相关的方法,后来我干脆自己胡乱猜想,我当时还曾以为是线程里面的数据冲突,崩溃就崩在[self.shops removeObjectAtIndex:indexPath.row];这句上,就是说可能是我删除的indexPath.row时候那一行已经删除了,我又猜想是不是delete的时候读数据跟写数据冲突,我给block里面所有跟控制器有关的指针的__weak关键字,我甚至煞笔地给tableView的数据源方法加了锁,同时给我block里面删除操作都lock起来,最后把自己都锁煞笔了(其实,我对锁的使用并不熟练,而且我只在多线程里面偶有用到)总之一下午倒腾总结起来就一个字:然并卵!

  好了,废话不多了,我这日记写的越来越像博客了0.0.。。。。

  最后:

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

        cell.shop = self.shops[indexPath.row];
        cell.myDelegate = self;
    __weak typeof(self) weakSelf = self;
    cell.rightButtons = @[[MGSwipeButton buttonWithTitle:@"删除" backgroundColor:[UIColor magentaColor] padding:50 callback:^BOOL(MGSwipeTableCell *sender) {}]];

之前我在这个数据源方法里面写的,block里面使用的indexPath都是数据源方法-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath中传进来的参数

     [weakSelf.shops removeObjectAtIndex:indexPath.row];      
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];

然后我仔细一看发现第三方的哪个cell  叫我传得button数组 button后面的方法里面 传的block回调有个参数sender  把cell  传进来了,我用lldb命令打印了一下 发现  两个indexPath 并不一样

  因为是第一次用这个框架,一开始我还奇怪他block 为什么要传个sender  我在数据源方法里面本来能拿到,不是多余的吗  然后我刚刚脑子一热就试了下 发现没有崩溃了。但是不知道 为什么偶尔会崩,没崩的时候两者indexPath是一样的  ,我又猜想应该是跟cell的重用机制有关吧。具体确定的原因尚不明确,不过好在问题总算解决了,哎,还有有种蛋蛋的忧伤 ,废了一整天 解决了这么一个小问题还没搞清楚本质。

  博文在此,日后必会解决地一清二楚,暂且记下,问题很小,我很菜,跟我一样的新手遇到了可以避免这坑,大神误入请略过。。。。

 

 














 

posted @ 2015-12-21 21:10  MysticCoder  阅读(1835)  评论(1编辑  收藏  举报