有關UITableViewCell的側滑刪除以及使用相關大神框架MGSwipeTableCell遇到的小問題

  提起筆,殊不知道從何寫起了,今天一成天都耗費在了這個可能根本不算是問題的小問題上,至今仍有一種蛋蛋的憂桑。。(噢,不是提筆,是鍵盤手T_T)html

  表格視圖在項目中就像是每日的屢見不鮮,在cell上添加側滑刪除功能這種需求也是遍地可見。而就是這麼一個家常菜卻坑了我一天,可能我是真的閒的蛋疼吧,好吧,其實,講道理仍是我太菜,人艱不拆。git

  好了廢話很少說,運用系統自帶的API實現側滑刪除功能其實很是簡單:github

 
//- (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的代理,實現它的一些代理方法;另外一種更方便就MGSwipeButtonspa

裏提供了一個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的重用機制有關吧。具體肯定的緣由尚不明確,不過好在問題總算解決了,哎,還有有種蛋蛋的憂傷 ,廢了一成天 解決了這麼一個小問題還沒搞清楚本質。

  博文在此,往後必會解決地一清二楚,暫且記下,問題很小,我很菜,跟我同樣的新手遇到了能夠避免這坑,大神誤入請略過。。。。

相關文章
相關標籤/搜索