10 Editing UITableView

562 views
408 views

Published on

编辑 UITableView,编辑模式,增加行,删除行,移动行

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
562
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
6
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

10 Editing UITableView

  1. 1. 编辑 UITableView范圣刚,princetoad@gmail.com, www.tfan.org
  2. 2. 编辑模式下的 Homepwner• 上⼀一节我们实现了把 BNRItem 的⼀一系列实例在 UITableView 中显⽰示的应⽤用• 下⼀一步我们将允许⽤用户和表格进⾏行交互 - 添加, 删除和移动表格的⾏行
  3. 3. 编辑模式
  4. 4. • UITableView 有⼀一个 editing 属性,并且当这个属性 被设置成 YES 的时候, UITableView 就进⼊入了编辑 模式。• ⼀一旦 table view 进⼊入编辑模式,表格中的⾏行就可 以被⽤用户操作。⽤用户可以改变⾏行的顺序,添加新 ⾏行,或者删除⾏行。但是编辑模式并不允许⽤用户编 辑表格⾏行本⾝身的内容• 怎么才能让⽤用户进⼊入编辑模式?我们将使⽤用在表 格的 header view 中的⼀一个按钮来切换编辑模式
  5. 5. header view• header view 出现在表 格的⼀一个 section 的顶 部,被⽤用来增加 section 范围或者 table 范围的标题和控件• 可以是任意 UIView 的 实例• 同样也有在 section 底 部的 footer view,原 理是⼀一样的
  6. 6. 我们⽤用到的 header view• 我们要创建的 header view 将会出现在 BNRItem 列 表的顶部,将会包含两个⼦子视图,都是 UIButton 的实例 • ⼀一个⽤用来切换编辑模式 • 另⼀一个⽤用来往表格中增加⼀一个新的 BNRItem• 我们将在 XIB ⽂文件中创建这个视图,然后 ItemsViewController 将在其需要显⽰示这个 header view 的时候解压这个 XIB ⽂文件
  7. 7. headerView 相关的变量和⽅方法 • ⾸首先我们来设置必要的代码 • 在 ItemsViewController.h 中为我们的 header view 声明⼀一个 UIView 类型的实例变量和三个新的⽅方法@interface ItemsViewController : UITableViewController{ IBOutlet UIView *headerView;}- (UIView *)headerView;- (IBAction)addNewItem:(id)sender;- (IBAction)toggleEdittingMode:(id)sender;
  8. 8. XIB ⽂文件:HeaderView• 新的 XIB ⽂文件(File -> New -> File, iOS -> User Interface, Emtpy, 保存为: HeaderView)• 和我们之前创建的所有 XIB ⽂文件都不同,这个 XIB ⽂文件和 view controller 的 view 没有任何关系• 作为 UITableViewController 的⼦子类, ItemsViewController 总是知道如何⽣生成它的 view• XIB ⽂文件通常被⽤用来为⼀一个 view controller ⽣生成 view,但是我们也可以在任何想要布局 view 对象 的时候使⽤用它
  9. 9. File’s Owner 的类• ⾸首先在 HeaderView.xib 中,选择 File’s Owner 对 象,然后把它的 Class 改 成 ItemsViewController• 然后拖曳⼀一个 UIView 到 canvas 区域,再拖曳两个 UIButton 的实例到这个视 图上• 调整这个 UIView 的⼤大⼩小并 按下图⽣生成连接
  10. 10. HeaderView 的布局和连接
  11. 11. view 的背景颜⾊色• 同时要把 UIView 实例 的颜⾊色改成完全透明• 在 view 的 attribute inspector 中,点击 Background 的颜⾊色选 择器,然后把 Opacity 拖到 0(clear color)
  12. 12. 加载 HeaderView.xib• 截⾄至⺫⽬目前,XIB ⽂文件的加载都是由 UIViewController 的实现⾃自动完成的• ⽐比如前⾯面的 TimeViewController 知道如何加载 TimeViewController.xib, 因为写在它的超类 (UIViewController)中的代码• 对于 HeaderView.xib, 我们⼿手动来写⼀一些代码让 ItemsViewController 加载这个 XIB ⽂文件• 要⼿手动加载 XIB ⽂文件,我们要⽤用到 NSBundle
  13. 13. NSBundle 和 mainBundle• NSBundle 类是 application 和 application bundle 之间的接⼝口• 当我们想要访问位于 application bundle 中的⽂文件 时,我们向 NSBundle 请求它• 当应⽤用程序启动时,⼀一个 NSBundle 的实例就被创 建,然后我们可以通过向 NSBundle 发送 mainBundle 消息获得指向这个实例的指针• ⼀一旦我们拿到了指向 main bundle 对象的指针, 我们就可以请求它加载⼀一个 XIB ⽂文件
  14. 14. 实现 headerView ⽅方法- (UIView *)headerView{ // 如果还没有加载 headerView ... if (!headerView) { // 加载 HeaderView.xib [[NSBundle mainBundle] loadNibNamed:@"HeaderView" owner:self options:nil]; } return headerView;} • ⾸首先注意⼀一下 loadNibNamed:owner:options: ⽅方法的参数 • 不需要加 XIB ⽂文件的扩展名,NSBundle 会⾃自⼰己搞定 • 我们是把 self 作为 XIB ⽂文件的 owner 传进去的,这就把 ItemsViewController 的实例填到了 XIB ⽂文件的 File’s Owner hole 了 • headerView 消息第⼀一次被发到 ItemViewController 时,它就会 加载 HeaderView.xib 并且在实例变量 headerView 中保存⼀一个 指向 view 对象的指针。当 view 上的按钮被按下时就会发消 息到 ItemViewController
  15. 15. 两个 header view 相关的⽅方法• 我们已经⽣生成了 headerView, 下⾯面就使它成为 table 的 header view。这需要在 ItemsViewController.m 中从 UITableViewDelegate protocol 实现两个⽅方法 • tableView:viewForHeaderInSection:, 返回⼀一个 view • tableView:heightForHeaderInSection:• 虽然这两个⽅方法在 protocol 中都是作为 optional 列出来的,但是想要 header view 的话,必须实现 他们
  16. 16. headerView 的加载// 实现两个⽅方法使 headerView 成为 table 的 header view- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{ return [self headerView];}- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ // header view 的⾼高度应该由 XIB ⽂文件中视图的⾼高度决定 return [[self headerView] bounds].size.height;} • 实现着两个⽅方法之后,当 UITableView 需要显⽰示它的 header view 时,就会发送这些消息给它的 delegate,也就是 ItemsViewController • 当 tableView:heightForHeaderInSection: 消息第⼀一次被发给 ItemsViewController 时,它会先发给⾃自⼰己⼀一个 headerView 消息。此 时,headerView 还是 nil,这将会引起 headerView 被从 XIB ⽂文件中被 加载
  17. 17. editing 属性• headerView 已经可以成为 table 的 header view 了,下⾯面来实现 toggleEditingMode: ⽅方法,也就是 我们点击按钮切换编辑状态的⽅方法• 我们可以直接切换 UITableView 的 editing 属性。 然⽽而,UITableViewController 也有⼀一个 editing 属 性。⼀一个 UITableViewController 实例会⾃自动设置 它的 table view 的 editing 属性来和它的 editing 属 性相匹配。该⽤用哪⼀一个?遵循 MVC 模式:和 controller 打交道,让 controller 去和 view 打交道
  18. 18. setEditing:animated:- (IBAction)toggleEdittingMode:(id)sender{ // 如果我们已经在编辑模式 if ([self isEditing]) { // 改变按钮的⽂文本以通知⽤用户状态 [sender setTitle:@"编辑" forState:UIControlStateNormal]; // 关闭编辑模式 [self setEditing:NO animated:YES]; } else { [sender setTitle:@"完成" forState:UIControlStateNormal]; // 进⼊入编辑模式 [self setEditing:YES animated:YES]; }} • 要设置 view controller 的 editing 属性,就给它发 送 setEditing:animated: 消息
  19. 19. Editing mode 下的 UITableView
  20. 20. 增加⾏行• 我们使⽤用 header view 中的⼀一个“新建”按钮来给表 格增加新⾏行,当按钮被按下时,⼀一个新⾏行将被增 加到 UITableView。• 是 UITableView 的 dataSource 来决定 table view 要 显⽰示的⾏行数,因此我们要确保 UITableView 显⽰示的 ⾏行数要和 dataSource 保存的⾏行数⼀一致• 因此我们在实现 addNewItem: 时,在往 table view 插⼊入⼀一⾏行前,要先把增加⼀一个新的 BNRItem 到 BNRItemStore 中
  21. 21. 实现 addNewItem:- (IBAction)addNewItem:(id)sender{// // 为第 0 个 section ⽣生成⼀一个新的 index path,最后⼀一⾏行// int lastRow = [[self tableView] numberOfRowsInSection:0]; BNRItem *newItem = [[BNRItemStore defaultStore] createItem]; int lastRow = [[[BNRItemStore defaultStore] allItems]indexOfObject:newItem]; NSIndexPath *ip = [NSIndexPath indexPathForItem:lastRow inSection:0]; // 把这个新⾏行插⼊入 table [[self tableView] insertRowsAtIndexPaths:[NSArray arrayWithObject:ip]withRowAnimation:UITableViewRowAnimationTop];} • 发送 tableView 消息给 UITableViewController 将返 回 controller 的 table view • 现在我们可以⾃自⼰己添加新⾏行了,可以把 init ⽅方法 中往 store 中压⼊入 5 个随机 items 的代码删掉了
  22. 22. 删除⾏行• 在删除模式下,带横线的红圈是删除控件,触控 其中⼀一个应该删除那⼀一⾏行。然后,此时触控删除 控件不会发⽣生任何事情。在 table view 将删除⼀一 ⾏行前,它会发送⼀一个有关拟删除的消息给它的数 据源,并且在扣动扳机前会等待⼀一个确认消息
  23. 23. • 在删除⼀一个单元格时,必须要做两件事情: • 从 UITableView 删除那⼀一⾏行 • 从 BNRItemStore 中删除和它关联的 BNRItem• BNRItemStore 必须知道如何从其⾃自⾝身删除⼀一个对 象,我们要在 BNRItemStore.h 中声明⼀一个新的⽅方 法:removeItem:
  24. 24. removeItem:@interface BNRItemStore : NSObject{ NSMutableArray *allItems;}+ (BNRItemStore *)defaultStore;- (NSArray *)allItems;- (BNRItem *)createItem;- (void)removeItem:(BNRItem *)p;- (void)removeItem:(BNRItem *)p{ [allItems removeObjectIdenticalTo:p];} • 可以使⽤用 removeObject: ⽅方法替换 removeObjectIdenticalTo: ,但是 要注意它们的区别 • removeObject: ⽅方法遍历数组的每个对象并发送给它们⼀一个 isEqual: 消息。类可以实现这个⽅方法根据⾃自⼰己的判断来返回 YES or NO
  25. 25. UITableViewCellEditingStyleDelete• 下⾯面我们实现 tableView:commitEditingStyle:forRowAtIndexPath:, ⼀一 个来⾃自 UITableViewDataSource protocol 的⽅方法。•当 tableView:commitEditingStyle:forRowAtIndexPath: 被发送给数据源时,两个额外的参数也被随着传 递 • 第⼀一个是 UITableViewCellEditingStyle, 在这⾥里是 UITableViewCellEditingStyleDelete • 另⼀一个参数是表格中⾏行的 NSIndexPath
  26. 26. 实现确认删除的⽅方法- (void)tableView:(UITableView *)tableViewcommitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{ if (editingStyle == UITableViewCellEditingStyleDelete) { BNRItemStore *ps = [BNRItemStore defaultStore]; NSArray *items = [ps allItems]; BNRItem *p = [items objectAtIndex:[indexPath row]]; [ps removeItem:p]; // 同时也把这⼀一⾏行从 table view 中删除 [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]withRowAnimation:UITableViewRowAnimationFade]; }} • 在 ItemsViewController.m 中实现这个⽅方法 • 让 BNRItemStore 删除正确的对象 • 通过发回给table view deleteRowsAtIndexPaths:withRowAnimation: 消息确认对 ⾏行的删除
  27. 27. 移动⾏行
  28. 28. • 要改变⼀一个 UITableView 中⾏行的顺序,要使⽤用来⾃自 UITableViewDataSource 的另外⼀一个⽅方法: tableView:moveRowAtIndexPath:toIndexPath:• 前⾯面我们看到要删除⼀一⾏行的话,要发送 deleteRowsAtIndexPaths:withRowAnimation: 给 UITableView 来确认删除• 移动⼀一⾏行的话,不需要确认,只是通过发送 tableView:moveRowAtIndexPath:toIndexPath: 消息 给它的数据源通知这个移动操作• 我们只需要捕捉这个消息来更新我们的数据源以 匹配新的顺序
  29. 29. moveItemAtIndex:toIndex ⽅方法 • 和删除⾏行⼀一样,我们在实现这个数据源⽅方法之前 先给 BNRItemStore ⼀一个⽅方法,⽤用于变更在它的 allItems 数组中的 BNRItem 的顺序。 • 声明和实现 moveItemAtIndex:toIndex: ⽅方法- (void)moveItemAtIndex:(int)from toIndex:(int)to{ if (from == to) { return; } // 得到被移动的对象的指针,以便我们可以把它重新插⼊入 BNRItem *p = [allItems objectAtIndex:from]; // 从数组中删除 [allItems removeObjectAtIndex:from]; // 在新的位置重新插⼊入 [allItems insertObject:p atIndex:to];}
  30. 30. tableView:moveRowAtIndexPath:toIndexPath • 在 ItemsViewController.m 中实现 tableView:moveRowAtIndexPath:toIndexPath: 来更新 store- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath*)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{ [[BNRItemStore defaultStore] moveItemAtIndex:[sourceIndexPath row]toIndex:[destinationIndexPath row]];}
  31. 31. 移动⼀一⾏行• 只要简单的实现 tableView:moveRowAtI ndexPath:toIndexPath: 就可以让重新排序的 控件出现,这是因为 Objective-C 语⾔言的特 性• UITableView 可以在运 ⾏行时询问它的数据源 是否实现了这个⽅方法

×