UITableViewCellEditingStyle的问题

(void)tableView:(UITableView *)aTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle 
            forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        NSDictionary *section = [data objectAtIndex:indexPath.section];
        if (section) {
            NSMutableArray *content = [section valueForKey:@"content"];
            if (content && indexPath.row < [content count]) {
                [content removeObjectAtIndex:indexPath.row];
            }
        }
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        NSDictionary *section = [data objectAtIndex:indexPath.section];
        if (section) {
            // Make a local reference to the editing view controller.
            EditingViewController *controller = self.editingViewController;
            NSMutableArray *content = [section valueForKey:@"content"];
            // A "nil" editingItem indicates the editor should create a new item.
            controller.editingItem = nil;
            // The group to which the new item should be added.
            controller.editingContent = content;
            controller.sectionName = [section valueForKey:@"name"];
            controller.editingTypes = [section valueForKey:@"types"];
            [self.navigationController pushViewController:controller animated:YES];
        }
    }

}


UITableView除了常规的选择模式(selection mode)外还有一个编辑模式(editing mode),在编辑模式中可实现删除,插入,多选,重排序等。

一.进入编辑模式

  • 通过直接设置UITableView的editing属性或向其发送setEditing:animated:消息,可将其置于编辑模式。
    
    self.tableview.editing = YES;
    [self.tableview setEditing:YES animated:YES];
    
  • UIViewController本身也有editing属性和setEditing:animated:方法,在当前视图控制器由导航控制器控制且导航栏中包含editButtonItem时,若UIViewController的editing为NO,则显示为”Edit”,若editing为YES,则显示为”Done”。


    可利用此按钮在设置UIViewController的editing状态时同时设置tableView的编辑状态。

    - (void)viewDidLoad {
        [super viewDidLoad];
        ....
        self.navigationItem.rightBarButtonItem = self.editButtonItem;
    }
    -(void)setEditing:(BOOL)editing animated:(BOOL)animated
    {
        [super setEditing:editing animated:animated];
        [self.tableView setEditing:editing animated:animated];
    }
  • 也可自定义其他按钮,将其响应设为修改tableView进入编辑模式。
    - (void)editAction:(id)sender
    {
       [self.tableView setEditing:YES animated:YES];
    }

    UITableView接收到setEditing:animated:消息时,会发送同样的消息到所有可见的cell,设置其编辑模式。

    二.插入和删除

  • 进入编辑模式后,UITableView向其DataSource发送tableView:canEditRowAtIndexPath:消息询问每个indexPath是否可编辑,在此方法中对不可以编辑的cell返回NO,可以编辑的cell返回YES,若全部可编辑,可不实现,大部分应用不实现此方法。
    -(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 1) {
            return NO;
        }
        return YES;
    }
  • 然后,UITableView 向其delegate发送tableView:editingStyleForRowAtIndexPath:消息询问EditingStyle,这里返回删除(UITableViewCellEditingStyleDelete)或者插入(UITableViewCellEditingStyleInsert);若不实现此方法,则默认为删除模式,即UITableViewCellEditingStyleDelete。
    - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return UITableViewCellEditingStyleDelete;
        //return UITableViewCellEditingStyleInsert;
    }

    当返回的是UITableViewCellEditingStyleDelete时,所有可编辑的Cell左侧都会显示红色的”减号”标示;

    点击左边的“减号”,减号会旋转90度变竖形,并且cell右侧出现”Delete”按钮。

    当返回的是UITableViewCellEditingStyleInsert时,在cell的左边会显示绿色”加号”按钮。

  • 当点击”Delete”按钮或者”加号”按钮时,UITableView向其DataSource发送tableView:commitEditingStyle:forRowAtIndexPath:消息,根据传递editingStyle来执行实际的删除或插入操作,其流程是先修改tableView的数据模型,向其中删除或插入对应数据项,然后再调整tableView的显示,删除或插入对应的cell。
    -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (editingStyle == UITableViewCellEditingStyleDelete)
        {
            [dataArray removeObjectAtIndex:indexPath.row];
            [tableview deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        }else if(editingStyle == UITableViewCellEditingStyleInsert)
        {
            [dataArray insertObject:@"new Item" atIndex:indexPath.row];
            [tableview insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        }
    }

    当要删除或插入section时,需调用deleteSections:withRowAnimation:insertSections:withRowAnimation:方法。

  • 插入删除流程的方法调用时序如图:

    三.重排序

  • 若当前tableView允许重排序,则会在每个cell的右侧出现三条灰色横线的控件,拖动此空间可将cell移动到不同的位置。重排序模式和删除/插入是并行的,即可在显示重排序空间的同时显示删除或插入控件。
  • 当tableView的dataSource实现tableView:moveRowAtIndexPath:toIndexPath:方法后,tableView进入编辑模式后就会在右侧显示“重排序”控件,如图所示。
  • 其消息处理流程为:
    1. tableView收到setEditing:animated:消息并将同样的消息发送给可见的cell。
    2. tableView向其DataSource发送tableView:canMoveRowAtIndexPath:消息,询问每一行是否可显示重排序空间,若为NO,则不显示,若为YES则显示。此方法不实现时默认所有行都可显示重排序控件。这时就会在每一行的右侧显示重排序控件。
      因为重排序没有使用向delegate发送tableView:editingStyleForRowAtIndexPath:消息询问编辑模式,所以其与删除、插入控件可同时存在,在一般情况下不应该同时出现,所以应实现了tableView:editingStyleForRowAtIndexPath:并返回UITableViewCellEditingStyleNone;若不实现tableView:editingStyleForRowAtIndexPath:则会默认使用删除模式,即右侧出现“排序”控件时,左侧会出现”删除”控件。
    3. 用户可拖动每行右侧的空间来移动该行的位置。
    4. 用户拖动某行经过目标行上方时,tableView会向delegate发送tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:(若delegate有实现)消息询问是否可移动到此位置(ProposedIndexPath),若不可移动到此位置则返回一个新的目的indexPath,可以的话直接将ProposedIndexPath返回即可。一般情况下不需实现此方法。
      -(NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
      {
          if (proposedDestinationIndexPath.row == 5) {
              return [NSIndexPath indexPathForRow:8 inSection:0];
      
          }
          return proposedDestinationIndexPath;
      }
    5. tableView向其DataSource发送tableView:moveRowAtIndexPath:toIndexPath:消息,在此方法中更改tableView的数据模型,移动里面数据项的位置。
      - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
          if(sourceIndexPath == destinationIndexPath)
              return;
          id object = [dataArray objectAtIndex:sourceIndexPath.row];
          [dataArray removeObjectAtIndex:sourceIndexPath.row];
      
          [dataArray insertObject:object atIndex:destinationIndexPath.row];
      }

    总体消息流程为:

    四.Swipe to Delete

  • 当用户在tableView的一行上滑动时,会在右侧直接出现删除按钮,点击删除按钮可删除此行。启用Swipe to Delete模式的条件时tableView的DataSource实现了tableView:commitEditingStyle:forRowAtIndexPath:方法;在iOS5中还要保证tableView的allowsMultipleSelectionDuringEditing属性不为YES(见后面解释)。
  • 满足上述条件后,当用户在tableView的行上滑动时,tableView会向自身发送setEditing:animated:消息进入编辑模式,同时向可见的cell发送setEditing:animated:消息,在当前滑动的行右侧会出现红色”Delete”按钮。
  • 同样在点击”Delete”按钮时会向tableView的DataSource发送tableView:commitEditingStyle:forRowAtIndexPath:消息,执行实际的删除操作。
  • “Delete”按钮上显示的文字可以更改,包括普通删除模式下的”Delete”按钮。若要显示不同的内容,可在tableView Delegate中实现tableView:titleForDeleteConfirmationButtonForRowAtIndexPath:方法,返回”Delete”按钮中需要显示的内容
  • tableView在向自身发送setEditing:animated:消息的前后,会向其delegate分别发送tableView:willBeginEditingRowAtIndexPath: ,tableView:didEndEditingRowAtIndexPath:消息。在这些方法中可相应更新tableView的显示。How does the Twitter iPhone app implement side swiping on a table?中通过实现tableView:willBeginEditingRowAtIndexPath:方法使得用户在tableView的行上swipe时可滑出菜单。

    五.多行选取模式

    在iphone自带的邮件程序中,点击编辑按钮后会出现使用”红勾”多选的效果,如图所示

    有几种方法可以实现这种效果

    1.苹果公共API
  • 在iOS5.0中UITableView增加了allowsMultipleSelectionDuringEditing属性和indexPathsForSelectedRows方法,allowsMultipleSelectionDuringEditing属性默认为NO,当此值为YES时,UITableView进入编辑模式不会向dataSource查询editStyle,而会直接每个Cell的左边显示圆形选择框,点击选择该行后圆形框里面为对勾。可使用indexPathsForSelectedRows方法获取到所有选择行的indexPath。
  • 苹果公司提供了使用此种方式的实例代码:TableMultiSelect
  • 注:当allowsMultipleSelectionDuringEditing属性为YES时,不管当前在不在编辑模式内,swipe to delete都不起作用,若要同时使用swipe to delete,需在完成选择任务后,将tableView的allowsMultipleSelectionDuringEditing恢复为NO。另外,多选”控件可与”重排序”控件同时出现。
    2.苹果私用API

    在iOS5之前,苹果并没有提供多行选取的API,但其内部确实实现了,我们可以通过使用私有API实现。

    3.完全定制方法

    一些文章中介绍了不使用tableView本身的方法而完全自己定制实现多选效果的方法。
    如:Table View Multi-Row Edit Mode
    Multiple row selection and editing in a UITableView

    参考:
    Table View Programming Guide for iOS – Inserting and Deleting Rows and Sections
    Table View Programming Guide for iOS – Managing the Reordering of Rows
    UITableView Class Reference
    UITableViewDelegate Protocol Reference
    UITableViewDataSource Protocol Reference
    UITableViewCell Class Reference
    How does the Twitter iPhone app implement side swiping on a table? 
    UITableView划动删除的实现
    UITableView多选删除,类似mail中的多选删除效果
    iPhone开发技巧之私有API(2)— UITableView
    iOS 5 Dev Warning: UITableView’s Multiple Select During Editing Doesn’t Work with Swipe to Delete
    Table View Multi-Row Edit Mode
    Multiple row selection and editing in a UITableView

     http://www.winddisk.com/2012/07/05/uitableview_edit_mod/原文地址
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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添加到视图中即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值