ios 编辑表格功能

编辑表格

添加编辑功能之后,表格就会变得丰富起来。编辑功能可以令表格里的静态信息变成能够滚动的互动式控件,从而使用户可以添加或者移除数据。虽说处理编辑功能所要的代码有些复杂,但同样的技术却可以反复运用在各种应用的程序中。我们只要掌握了“进入编辑状态”、“离开编辑状态”,以及“实现撤销功能”等基础知识,就能把他们移用到其他程序上面。
效果图
主要代码如下:
@implementation ViewController
{
    NSMutableArray *items;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.view.backgroundColor = [UIColor whiteColor];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
    self.tableView.rowHeight = 20;
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    self.navigationItem.rightBarButtonItem = self.editButtonItem;
    items = [NSMutableArray array];
    for (int i=0; i<20; i++) {
        [items addObject:[NSString stringWithFormat:@"%d",i]];
    }
    
    [UIApplication sharedApplication].applicationSupportsShakeToEdit = YES;
    [self setBarButtonItems];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return items.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
    cell.textLabel.text = items[indexPath.row];
    return cell;
}

- (void)setBarButtonItems
{
    if (self.undoManager.isUndoing || self.undoManager.isRedoing) {
        [self performSelector:@selector(setBarButtonItems) withObject:nil afterDelay:0.1f];
        return;
    }
    UIBarButtonItem *undo = SYSBARBUTTON_TARGET(UIBarButtonSystemItemUndo, self.undoManager, @selector(undo));
    undo.enabled = self.undoManager.canUndo;
    
    UIBarButtonItem *redo = SYSBARBUTTON_TARGET(UIBarButtonSystemItemRedo, self.undoManager, @selector(redo));
    redo.enabled = self.undoManager.canRedo;
    UIBarButtonItem *add = SYSBARBUTTON_TARGET(UIBarButtonSystemItemAdd, self, @selector(addItem:));
    self.navigationItem.leftBarButtonItems = @[add,undo,redo];

}

- (void)setEditing:(BOOL)editing animated:(BOOL)animated{
    [super setEditing:editing animated:animated];
    
    [self.tableView setEditing:editing animated:animated];
    
    NSIndexPath *path = [self.tableView indexPathForSelectedRow];
    if (path) {
        [self.tableView deselectRowAtIndexPath:path animated:YES];
        
    }
    
    [self setBarButtonItems];
}

- (void)updateItemAtIndexPath:(NSIndexPath *)indexPath withObject:(id)object
{
    id undoObject = object ? nil : items[indexPath.row];
    [[self.undoManager prepareWithInvocationTarget:self] updateItemAtIndexPath:indexPath withObject:undoObject];
    [self.tableView beginUpdates];
    
    if (!object) {
        [items removeObjectAtIndex:indexPath.row];
        [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
    }else{
        [items insertObject:object atIndex:indexPath.row];
        [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
    }
    
    [self.tableView endUpdates];
    [self performSelector:@selector(setBarButtonItems) withObject:nil afterDelay:0.1f];
    
}

- (void)addItem:(id)sender
{
    NSIndexPath *newPath = [NSIndexPath indexPathForRow:items.count inSection:0];
    NSInteger count = items.count;
    count ++;
    [self updateItemAtIndexPath:newPath withObject:[NSString stringWithFormat:@"%ld",count]];
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self updateItemAtIndexPath:indexPath withObject:nil];
    
}

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
    if (sourceIndexPath.row == destinationIndexPath.row) {
        return;
    }
    
    [[self.undoManager prepareWithInvocationTarget:self] tableView:self.tableView moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath];
    
    id item = [items objectAtIndex:sourceIndexPath.row];
    [items removeObjectAtIndex:sourceIndexPath.row];
    [items insertObject:item atIndex:destinationIndexPath.row];
    
    if (self.undoManager.isUndoing || self.undoManager.isRedoing) {
        [self.tableView beginUpdates];
        [self.tableView deleteRowsAtIndexPaths:@[sourceIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
        [self.tableView insertRowsAtIndexPaths:@[destinationIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
        [self.tableView endUpdates];
    }
    [self performSelector:@selector(setBarButtonItems) withObject:nil afterDelay:0.1f];
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [self resignFirstResponder];
}

1、添加撤销功能

Cocoa Touch所提供的NSUndoManager类可用来反转用户的操作。在默认的情况下,每个应用程序的视图都会提供一份共享的撤销管理器。开发者可以使用这份共享的管理器,也可以自己创建新的。
UIResponder类的所有子类都能找到响应链中最近的那个撤销管理器。这就是说,如果在视图控制器中使用视图的NSUndoManager,那么只需通过控制器的undoManager属性,就能找到那个NSUndoManager了。这是个很方便的特性,因为开发者只需要给主视图控制器添加撤销功能,就能领其中的说有子视图西东具备该功能。
撤销管理器里面可以保存任意数量的撤销动作。开发者可以指定栈的深度。栈越深,所用的内存就越多。许多程序在内存比较紧张的时候都会只支持3、5或者10级撤销。栈中的每个动作既可以是由许多撤销操作所组成的复杂动作,又可以是例子中延时的那种简单的动作。
这个例子用撤销管理器来实现与“添加单元格”、“删除单元格”以及“移动单元格”这三种操作有关的撤销及重做功能。用户可以通过Undo和Redo按钮在自己的编辑历程之中游走。

2、实现撤销功能

例子采用同一个方法来添加及移除条目,这个方法就是updateItemAtIndexPath:withObject:。这个方法的运作方式为:如果传入的对象不是nil,那么就把它插入到索引路径中;如果是nil,则把位于该索引路径处的条目删除。
这样处理用户的请求似乎有点奇怪,因为我们需要编写一个方法,而且在方法里还要做一次判断,不过这样做其背后是有原因的。这种做法能够为撤销功能提供统一的基础代码,使得开发者可以更方便的将其同撤销管理器相集成。
因此,该方法有两件事要做。首先,它会准备好与撤销操作有关的行为。也就是说,它会告诉撤销管理器目前所运用的这项编辑操作将来应该如何还原。其次,他要执行实际的编辑操作,也就是修改items数组、更新表格,并更新导航栏中的按钮。
setBarButtonItems方法要控制Undo与Redo按钮的状态。该方法会检查当前正在活动的撤销管理器,看看撤销栈中是否提供了能够撤销及能够重做的动作。如果有,就启用相应的按钮。
这里提供了晃动撤销的功能。它在viewDidLoad方法里面把应用程序委托的appLicationSupportsShakeToEdit属性设为YES。另外也请大家注意,为了实现撤销的功能,我们调用了与第一响应者有关的两个方法,当表格视图即将出现在屏幕上面时,令其变成第一响应者,而当它要从屏幕中消失时,则令其放弃第一响应者的身份。

3、显示移除单元格所用的控件

调用[self.tableView setEditing:YES animated:YES] 方法,即可令表格把移除单元格所用的控件显示出来。这会更新表格的editing属性,并且会在每个单元格里显示出红色的移除控件。动画效果是可选的,有一条经验:如果要把程序从一个状态切换到另一个状态,那么应该在iOS界面中展示动画效果,使得用户意识到屏幕上的状态正在变化。

4、处理删除请求

删除表中的某一行时,表格会执行tableView:commitEditingStyle:forRowAtIndexPath:回调,以便将这一操作告知应用程序。从表格上移除某个条目只是令其不显示出来,这并不会修改底层的数据。除非开发者把这个条目也从数据源里移除,否则下次刷新表格的时候,这个已删除的条目还是会显示出来。我们可以在该方法里协调表格与数据源之间的关系,并对“用户想要删除这一行”的请求作出相应。
在本例中,我们可以从给数据源方法提供内容的数据结构里删除一项条目,而在真实的应用程序中,则可以执行诸如删除文件或移除联系人等操作,以响应用户的编辑请求。
我们可以把添加某行及删除某恒等操作放在beginUpdates方法之后,并放在配套的endUpdates方法之前,从而令系统能够以动画效果 来同时显示这些操作。

5、通过滑动手势删除单元格

要想从UITableView事=实例中移除条目,Swipe手势是个很简洁的方法。只需要实现tableView:commitEditingStyle:forRowAtIndexPath:方法,即可启用Swipe功能。表格会处理好剩下的事情。
用户将某个单元格从右迅速向左方拖拽,这就是针对单元格的Swipe手势,单元格右侧会显示出长方形的Delete按钮,用以确认此操作,不过左侧并不会出现表示移除单元格功能的那个红色圆形控件。
当用户执行Swipe操作并确认之后,我们就可以在tableView:commitEditingStyle:forRowAtIndexPath:方法里更新数据了,这与编辑模式下删除单元格是一样的。

6、调整单元格的顺序


如果能直接调整表格中各单元格的顺序,用户就可以获得更大的自由度了。用户可以通过重排单元格顺序来调整各项待办事物的优先级,或是在播放清单中选择首先要听的歌曲等。iOS的表格内置了重新排列顺序的功能,开发者很容易就能将其集成到应用程序中。
与通过滑动手势删除单元格一样,单元格重排功能是否启用也取决于开发者是否实现了某个方法。这个方法就是tableView:moveRowAtIndexPath:toIndexPath:,它可以将表格的数据源与屏幕上所发生的变化相同步,这与删除单元格时回调tableView:commitEditingStyle:forRowAtIndexPath:方法类似。添加这一方法即可调整单元格顺序。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值