SlideShare a Scribd company logo
1 of 31
Download to read offline
编辑 UITableView




范圣刚,princetoad@gmail.com, www.tfan.org
编辑模式下的 Homepwner




• 上⼀一节我们实现了把 BNRItem 的⼀一系列实例在
 UITableView 中显⽰示的应⽤用

• 下⼀一步我们将允许⽤用户和表格进⾏行交互 - 添加,
 删除和移动表格的⾏行
编辑模式
• UITableView 有⼀一个 editing 属性,并且当这个属性
 被设置成 YES 的时候, UITableView 就进⼊入了编辑
 模式。

• ⼀一旦 table view 进⼊入编辑模式,表格中的⾏行就可
 以被⽤用户操作。⽤用户可以改变⾏行的顺序,添加新
 ⾏行,或者删除⾏行。但是编辑模式并不允许⽤用户编
 辑表格⾏行本⾝身的内容

• 怎么才能让⽤用户进⼊入编辑模式?我们将使⽤用在表
 格的 header view 中的⼀一个按钮来切换编辑模式
header view
• header view 出现在表
 格的⼀一个 section 的顶
 部,被⽤用来增加
 section 范围或者 table
 范围的标题和控件

• 可以是任意 UIView 的
 实例

• 同样也有在 section 底
 部的 footer view,原
 理是⼀一样的
我们⽤用到的 header view
• 我们要创建的 header view 将会出现在 BNRItem 列
 表的顶部,将会包含两个⼦子视图,都是 UIButton
 的实例
 • ⼀一个⽤用来切换编辑模式
 • 另⼀一个⽤用来往表格中增加⼀一个新的 BNRItem
• 我们将在 XIB ⽂文件中创建这个视图,然后
 ItemsViewController 将在其需要显⽰示这个 header
 view 的时候解压这个 XIB ⽂文件
headerView 相关的变量和⽅方法
 • ⾸首先我们来设置必要的代码
 • 在 ItemsViewController.h 中为我们的 header view
   声明⼀一个 UIView 类型的实例变量和三个新的⽅方法



@interface ItemsViewController : UITableViewController
{
    IBOutlet UIView *headerView;
}

- (UIView *)headerView;
- (IBAction)addNewItem:(id)sender;
- (IBAction)toggleEdittingMode:(id)sender;
XIB ⽂文件:HeaderView
• 新的 XIB ⽂文件(File -> New -> File, iOS -> User
 Interface, Emtpy, 保存为: HeaderView)
• 和我们之前创建的所有 XIB ⽂文件都不同,这个 XIB
 ⽂文件和 view controller 的 view 没有任何关系
• 作为 UITableViewController 的⼦子类,
 ItemsViewController 总是知道如何⽣生成它的 view
• XIB ⽂文件通常被⽤用来为⼀一个 view controller ⽣生成
 view,但是我们也可以在任何想要布局 view 对象
 的时候使⽤用它
File’s Owner 的类
• ⾸首先在 HeaderView.xib
 中,选择 File’s Owner 对
 象,然后把它的 Class 改
 成 ItemsViewController

• 然后拖曳⼀一个 UIView 到
 canvas 区域,再拖曳两个
 UIButton 的实例到这个视
 图上

• 调整这个 UIView 的⼤大⼩小并
 按下图⽣生成连接
HeaderView 的布局和连接
view 的背景颜⾊色
• 同时要把 UIView 实例
 的颜⾊色改成完全透明

• 在 view 的 attribute
 inspector 中,点击
 Background 的颜⾊色选
 择器,然后把 Opacity
 拖到 0(clear color)
加载 HeaderView.xib
• 截⾄至⺫⽬目前,XIB ⽂文件的加载都是由
 UIViewController 的实现⾃自动完成的
• ⽐比如前⾯面的 TimeViewController 知道如何加载
 TimeViewController.xib, 因为写在它的超类
 (UIViewController)中的代码
• 对于 HeaderView.xib, 我们⼿手动来写⼀一些代码让
 ItemsViewController 加载这个 XIB ⽂文件
• 要⼿手动加载 XIB ⽂文件,我们要⽤用到 NSBundle
NSBundle 和 mainBundle
• NSBundle 类是 application 和 application bundle
 之间的接⼝口
• 当我们想要访问位于 application bundle 中的⽂文件
 时,我们向 NSBundle 请求它
• 当应⽤用程序启动时,⼀一个 NSBundle 的实例就被创
 建,然后我们可以通过向 NSBundle 发送
 mainBundle 消息获得指向这个实例的指针
• ⼀一旦我们拿到了指向 main bundle 对象的指针,
 我们就可以请求它加载⼀一个 XIB ⽂文件
实现 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
两个 header view 相关的⽅方法
• 我们已经⽣生成了 headerView, 下⾯面就使它成为
 table 的 header view。这需要在
 ItemsViewController.m 中从 UITableViewDelegate
 protocol 实现两个⽅方法
 • tableView:viewForHeaderInSection:, 返回⼀一个 view
 • tableView:heightForHeaderInSection:
• 虽然这两个⽅方法在 protocol 中都是作为 optional
 列出来的,但是想要 header view 的话,必须实现
 他们
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 ⽂文件中被
    加载
editing 属性
• headerView 已经可以成为 table 的 header view
 了,下⾯面来实现 toggleEditingMode: ⽅方法,也就是
 我们点击按钮切换编辑状态的⽅方法
• 我们可以直接切换 UITableView 的 editing 属性。
 然⽽而,UITableViewController 也有⼀一个 editing 属
 性。⼀一个 UITableViewController 实例会⾃自动设置
 它的 table view 的 editing 属性来和它的 editing 属
 性相匹配。该⽤用哪⼀一个?遵循 MVC 模式:和
 controller 打交道,让 controller 去和 view 打交道
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: 消息
Editing mode 下的 UITableView
增加⾏行
• 我们使⽤用 header view 中的⼀一个“新建”按钮来给表
 格增加新⾏行,当按钮被按下时,⼀一个新⾏行将被增
 加到 UITableView。
• 是 UITableView 的 dataSource 来决定 table view 要
 显⽰示的⾏行数,因此我们要确保 UITableView 显⽰示的
 ⾏行数要和 dataSource 保存的⾏行数⼀一致
• 因此我们在实现 addNewItem: 时,在往 table view
 插⼊入⼀一⾏行前,要先把增加⼀一个新的 BNRItem 到
 BNRItemStore 中
实现 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 的代码删掉了
删除⾏行
• 在删除模式下,带横线的红圈是删除控件,触控
 其中⼀一个应该删除那⼀一⾏行。然后,此时触控删除
 控件不会发⽣生任何事情。在 table view 将删除⼀一
 ⾏行前,它会发送⼀一个有关拟删除的消息给它的数
 据源,并且在扣动扳机前会等待⼀一个确认消息
• 在删除⼀一个单元格时,必须要做两件事情:
 • 从 UITableView 删除那⼀一⾏行
 • 从 BNRItemStore 中删除和它关联的 BNRItem
• BNRItemStore 必须知道如何从其⾃自⾝身删除⼀一个对
 象,我们要在 BNRItemStore.h 中声明⼀一个新的⽅方
 法:removeItem:
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
UITableViewCellEditingStyleDelete
• 下⾯面我们实现
 tableView:commitEditingStyle:forRowAtIndexPath:, ⼀一
 个来⾃自 UITableViewDataSource protocol 的⽅方法。
•当
 tableView:commitEditingStyle:forRowAtIndexPath:
 被发送给数据源时,两个额外的参数也被随着传
 递
 • 第⼀一个是 UITableViewCellEditingStyle, 在这⾥里是
  UITableViewCellEditingStyleDelete

 • 另⼀一个参数是表格中⾏行的 NSIndexPath
实现确认删除的⽅方法
- (void)tableView:(UITableView *)tableView
commitEditingStyle:(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: 消息确认对
           ⾏行的删除
移动⾏行
• 要改变⼀一个 UITableView 中⾏行的顺序,要使⽤用来⾃自
 UITableViewDataSource 的另外⼀一个⽅方法:
 tableView:moveRowAtIndexPath:toIndexPath:

• 前⾯面我们看到要删除⼀一⾏行的话,要发送
 deleteRowsAtIndexPaths:withRowAnimation: 给
 UITableView 来确认删除

• 移动⼀一⾏行的话,不需要确认,只是通过发送
 tableView:moveRowAtIndexPath:toIndexPath: 消息
 给它的数据源通知这个移动操作

• 我们只需要捕捉这个消息来更新我们的数据源以
 匹配新的顺序
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];
}
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]];
}
移动⼀一⾏行
• 只要简单的实现
 tableView:moveRowAtI
 ndexPath:toIndexPath:
 就可以让重新排序的
 控件出现,这是因为
 Objective-C 语⾔言的特
 性

• UITableView 可以在运
 ⾏行时询问它的数据源
 是否实现了这个⽅方法

More Related Content

Similar to 10 Editing UITableView

11 UINavigationController
11 UINavigationController11 UINavigationController
11 UINavigationControllerTom Fan
 
13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View Controller13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View ControllerTom Fan
 
06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollView06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollViewTom Fan
 
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAPiOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAPMing-Sian Lin
 
15 Subclassing UITableViewCell
15 Subclassing UITableViewCell15 Subclassing UITableViewCell
15 Subclassing UITableViewCellTom Fan
 
16 CoreData
16 CoreData16 CoreData
16 CoreDataTom Fan
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001rainx1982
 
105-2 iOS程式設計(八)
105-2 iOS程式設計(八)105-2 iOS程式設計(八)
105-2 iOS程式設計(八)Hao Lee
 
08 Notification and Rotation
08 Notification and Rotation08 Notification and Rotation
08 Notification and RotationTom Fan
 
01 A Simple iOS Application
01 A Simple iOS Application01 A Simple iOS Application
01 A Simple iOS ApplicationTom Fan
 
Vlog02 [eng sub]什麼是controller和如何在asp.net核心中創建controller?-what is controller ...
Vlog02  [eng sub]什麼是controller和如何在asp.net核心中創建controller?-what is controller ...Vlog02  [eng sub]什麼是controller和如何在asp.net核心中創建controller?-what is controller ...
Vlog02 [eng sub]什麼是controller和如何在asp.net核心中創建controller?-what is controller ...SernHao TV
 
12 Camera
12 Camera12 Camera
12 CameraTom Fan
 
Asp.net mvc網站的從無到有
Asp.net mvc網站的從無到有Asp.net mvc網站的從無到有
Asp.net mvc網站的從無到有Wade Huang
 

Similar to 10 Editing UITableView (20)

11 UINavigationController
11 UINavigationController11 UINavigationController
11 UINavigationController
 
I os 09
I os 09I os 09
I os 09
 
I os 10
I os 10I os 10
I os 10
 
13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View Controller13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View Controller
 
06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollView06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollView
 
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAPiOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
 
15 Subclassing UITableViewCell
15 Subclassing UITableViewCell15 Subclassing UITableViewCell
15 Subclassing UITableViewCell
 
16 CoreData
16 CoreData16 CoreData
16 CoreData
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001
 
I os 07
I os 07I os 07
I os 07
 
I os 08
I os 08I os 08
I os 08
 
105-2 iOS程式設計(八)
105-2 iOS程式設計(八)105-2 iOS程式設計(八)
105-2 iOS程式設計(八)
 
I os 01
I os 01I os 01
I os 01
 
I os 16
I os 16I os 16
I os 16
 
I os 02
I os 02I os 02
I os 02
 
08 Notification and Rotation
08 Notification and Rotation08 Notification and Rotation
08 Notification and Rotation
 
01 A Simple iOS Application
01 A Simple iOS Application01 A Simple iOS Application
01 A Simple iOS Application
 
Vlog02 [eng sub]什麼是controller和如何在asp.net核心中創建controller?-what is controller ...
Vlog02  [eng sub]什麼是controller和如何在asp.net核心中創建controller?-what is controller ...Vlog02  [eng sub]什麼是controller和如何在asp.net核心中創建controller?-what is controller ...
Vlog02 [eng sub]什麼是controller和如何在asp.net核心中創建controller?-what is controller ...
 
12 Camera
12 Camera12 Camera
12 Camera
 
Asp.net mvc網站的從無到有
Asp.net mvc網站的從無到有Asp.net mvc網站的從無到有
Asp.net mvc網站的從無到有
 

More from Tom Fan

PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统Tom Fan
 
HTML5 Web workers
HTML5 Web workersHTML5 Web workers
HTML5 Web workersTom Fan
 
Web sockets
Web socketsWeb sockets
Web socketsTom Fan
 
Semantics
SemanticsSemantics
SemanticsTom Fan
 
Multimedia
MultimediaMultimedia
MultimediaTom Fan
 
Intro to-html5
Intro to-html5Intro to-html5
Intro to-html5Tom Fan
 
Html5 history
Html5 historyHtml5 history
Html5 historyTom Fan
 
Geolocation
GeolocationGeolocation
GeolocationTom Fan
 
File api
File apiFile api
File apiTom Fan
 
Deviceaccess
DeviceaccessDeviceaccess
DeviceaccessTom Fan
 
Webstorage
WebstorageWebstorage
WebstorageTom Fan
 
Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分Tom Fan
 
AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状Tom Fan
 
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发Tom Fan
 
Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Tom Fan
 
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型Tom Fan
 
18 NSUserDefaults
18 NSUserDefaults18 NSUserDefaults
18 NSUserDefaultsTom Fan
 
17 Localization
17 Localization17 Localization
17 LocalizationTom Fan
 

More from Tom Fan (20)

PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统
 
HTML5 Web workers
HTML5 Web workersHTML5 Web workers
HTML5 Web workers
 
Web sockets
Web socketsWeb sockets
Web sockets
 
Storage
StorageStorage
Storage
 
Semantics
SemanticsSemantics
Semantics
 
Multimedia
MultimediaMultimedia
Multimedia
 
Intro to-html5
Intro to-html5Intro to-html5
Intro to-html5
 
Html5 history
Html5 historyHtml5 history
Html5 history
 
Geolocation
GeolocationGeolocation
Geolocation
 
File api
File apiFile api
File api
 
Deviceaccess
DeviceaccessDeviceaccess
Deviceaccess
 
Css3
Css3Css3
Css3
 
Webstorage
WebstorageWebstorage
Webstorage
 
Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分
 
AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状
 
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发
 
Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发
 
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型
 
18 NSUserDefaults
18 NSUserDefaults18 NSUserDefaults
18 NSUserDefaults
 
17 Localization
17 Localization17 Localization
17 Localization
 

10 Editing UITableView

  • 2. 编辑模式下的 Homepwner • 上⼀一节我们实现了把 BNRItem 的⼀一系列实例在 UITableView 中显⽰示的应⽤用 • 下⼀一步我们将允许⽤用户和表格进⾏行交互 - 添加, 删除和移动表格的⾏行
  • 4. • UITableView 有⼀一个 editing 属性,并且当这个属性 被设置成 YES 的时候, UITableView 就进⼊入了编辑 模式。 • ⼀一旦 table view 进⼊入编辑模式,表格中的⾏行就可 以被⽤用户操作。⽤用户可以改变⾏行的顺序,添加新 ⾏行,或者删除⾏行。但是编辑模式并不允许⽤用户编 辑表格⾏行本⾝身的内容 • 怎么才能让⽤用户进⼊入编辑模式?我们将使⽤用在表 格的 header view 中的⼀一个按钮来切换编辑模式
  • 5. header view • header view 出现在表 格的⼀一个 section 的顶 部,被⽤用来增加 section 范围或者 table 范围的标题和控件 • 可以是任意 UIView 的 实例 • 同样也有在 section 底 部的 footer view,原 理是⼀一样的
  • 6. 我们⽤用到的 header view • 我们要创建的 header view 将会出现在 BNRItem 列 表的顶部,将会包含两个⼦子视图,都是 UIButton 的实例 • ⼀一个⽤用来切换编辑模式 • 另⼀一个⽤用来往表格中增加⼀一个新的 BNRItem • 我们将在 XIB ⽂文件中创建这个视图,然后 ItemsViewController 将在其需要显⽰示这个 header view 的时候解压这个 XIB ⽂文件
  • 7. headerView 相关的变量和⽅方法 • ⾸首先我们来设置必要的代码 • 在 ItemsViewController.h 中为我们的 header view 声明⼀一个 UIView 类型的实例变量和三个新的⽅方法 @interface ItemsViewController : UITableViewController { IBOutlet UIView *headerView; } - (UIView *)headerView; - (IBAction)addNewItem:(id)sender; - (IBAction)toggleEdittingMode:(id)sender;
  • 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. File’s Owner 的类 • ⾸首先在 HeaderView.xib 中,选择 File’s Owner 对 象,然后把它的 Class 改 成 ItemsViewController • 然后拖曳⼀一个 UIView 到 canvas 区域,再拖曳两个 UIButton 的实例到这个视 图上 • 调整这个 UIView 的⼤大⼩小并 按下图⽣生成连接
  • 11. view 的背景颜⾊色 • 同时要把 UIView 实例 的颜⾊色改成完全透明 • 在 view 的 attribute inspector 中,点击 Background 的颜⾊色选 择器,然后把 Opacity 拖到 0(clear color)
  • 12. 加载 HeaderView.xib • 截⾄至⺫⽬目前,XIB ⽂文件的加载都是由 UIViewController 的实现⾃自动完成的 • ⽐比如前⾯面的 TimeViewController 知道如何加载 TimeViewController.xib, 因为写在它的超类 (UIViewController)中的代码 • 对于 HeaderView.xib, 我们⼿手动来写⼀一些代码让 ItemsViewController 加载这个 XIB ⽂文件 • 要⼿手动加载 XIB ⽂文件,我们要⽤用到 NSBundle
  • 13. NSBundle 和 mainBundle • NSBundle 类是 application 和 application bundle 之间的接⼝口 • 当我们想要访问位于 application bundle 中的⽂文件 时,我们向 NSBundle 请求它 • 当应⽤用程序启动时,⼀一个 NSBundle 的实例就被创 建,然后我们可以通过向 NSBundle 发送 mainBundle 消息获得指向这个实例的指针 • ⼀一旦我们拿到了指向 main bundle 对象的指针, 我们就可以请求它加载⼀一个 XIB ⽂文件
  • 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. 两个 header view 相关的⽅方法 • 我们已经⽣生成了 headerView, 下⾯面就使它成为 table 的 header view。这需要在 ItemsViewController.m 中从 UITableViewDelegate protocol 实现两个⽅方法 • tableView:viewForHeaderInSection:, 返回⼀一个 view • tableView:heightForHeaderInSection: • 虽然这两个⽅方法在 protocol 中都是作为 optional 列出来的,但是想要 header view 的话,必须实现 他们
  • 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. editing 属性 • headerView 已经可以成为 table 的 header view 了,下⾯面来实现 toggleEditingMode: ⽅方法,也就是 我们点击按钮切换编辑状态的⽅方法 • 我们可以直接切换 UITableView 的 editing 属性。 然⽽而,UITableViewController 也有⼀一个 editing 属 性。⼀一个 UITableViewController 实例会⾃自动设置 它的 table view 的 editing 属性来和它的 editing 属 性相匹配。该⽤用哪⼀一个?遵循 MVC 模式:和 controller 打交道,让 controller 去和 view 打交道
  • 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. Editing mode 下的 UITableView
  • 20. 增加⾏行 • 我们使⽤用 header view 中的⼀一个“新建”按钮来给表 格增加新⾏行,当按钮被按下时,⼀一个新⾏行将被增 加到 UITableView。 • 是 UITableView 的 dataSource 来决定 table view 要 显⽰示的⾏行数,因此我们要确保 UITableView 显⽰示的 ⾏行数要和 dataSource 保存的⾏行数⼀一致 • 因此我们在实现 addNewItem: 时,在往 table view 插⼊入⼀一⾏行前,要先把增加⼀一个新的 BNRItem 到 BNRItemStore 中
  • 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. 删除⾏行 • 在删除模式下,带横线的红圈是删除控件,触控 其中⼀一个应该删除那⼀一⾏行。然后,此时触控删除 控件不会发⽣生任何事情。在 table view 将删除⼀一 ⾏行前,它会发送⼀一个有关拟删除的消息给它的数 据源,并且在扣动扳机前会等待⼀一个确认消息
  • 23. • 在删除⼀一个单元格时,必须要做两件事情: • 从 UITableView 删除那⼀一⾏行 • 从 BNRItemStore 中删除和它关联的 BNRItem • BNRItemStore 必须知道如何从其⾃自⾝身删除⼀一个对 象,我们要在 BNRItemStore.h 中声明⼀一个新的⽅方 法:removeItem:
  • 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. UITableViewCellEditingStyleDelete • 下⾯面我们实现 tableView:commitEditingStyle:forRowAtIndexPath:, ⼀一 个来⾃自 UITableViewDataSource protocol 的⽅方法。 •当 tableView:commitEditingStyle:forRowAtIndexPath: 被发送给数据源时,两个额外的参数也被随着传 递 • 第⼀一个是 UITableViewCellEditingStyle, 在这⾥里是 UITableViewCellEditingStyleDelete • 另⼀一个参数是表格中⾏行的 NSIndexPath
  • 26. 实现确认删除的⽅方法 - (void)tableView:(UITableView *)tableView commitEditingStyle:(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: 消息确认对 ⾏行的删除
  • 28. • 要改变⼀一个 UITableView 中⾏行的顺序,要使⽤用来⾃自 UITableViewDataSource 的另外⼀一个⽅方法: tableView:moveRowAtIndexPath:toIndexPath: • 前⾯面我们看到要删除⼀一⾏行的话,要发送 deleteRowsAtIndexPaths:withRowAnimation: 给 UITableView 来确认删除 • 移动⼀一⾏行的话,不需要确认,只是通过发送 tableView:moveRowAtIndexPath:toIndexPath: 消息 给它的数据源通知这个移动操作 • 我们只需要捕捉这个消息来更新我们的数据源以 匹配新的顺序
  • 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. 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. 移动⼀一⾏行 • 只要简单的实现 tableView:moveRowAtI ndexPath:toIndexPath: 就可以让重新排序的 控件出现,这是因为 Objective-C 语⾔言的特 性 • UITableView 可以在运 ⾏行时询问它的数据源 是否实现了这个⽅方法