SlideShare una empresa de Scribd logo
1 de 73
Descargar para leer sin conexión
UINavigationController




范圣刚,princetoad@gmail.com, www.tfan.org
• 前⾯面我们看到了 UITabBarController 能够允许⽤用户访问不
 同的 screen,标签栏控件适⽤用于⼏几个 screen 之间互不依
 赖的情况

• 如果我们想让⽤用户在⼀一些相关联的 screen 之间移动,我
 们就可以使⽤用 UINavigationController

• ⽐比如 iOS 的 “设置”应⽤用有多个相关的 screen:⼀一系列的
 设置和针对每个设置的详细⻚页⾯面,对于每个详细项还有
 ⼀一个选项⻚页⾯面,这种类型的界⾯面称作:drill-down
 interface

• 这⼀一个主题我们就使⽤用 UINavigationController 给
 Homepwner 添加⼀一个 drill-down interface, 让⽤用户能够查
 看和编辑⼀一个 BNRItem 的详细信息
增加了 UINavigationController 的
      Homepwner
UINavigationController
• 当应⽤用程序呈现多屏信息时,
 UINavigationController 维护了⼀一个这些屏幕的堆
 栈(stack)。

• 每⼀一个 screen 是 UIViewController 的 view,这个
 stack 就是 view controller 的数组。

• 当⼀一个 UIViewController 位于 stack 的顶部时,它
 的 view 就可⻅见。
• 在初始化⼀一个 UINavigationController 的实例时,
 我们给它⼀一个 UIViewController。这个
 UIViewController 是 navigation controller 的 root
 view controller。

• 这个 root view controller 总是在堆栈的底部。应⽤用
 程序运⾏行时,可以往堆栈中压⼊入更多的 view
 controller.
• UITabBarController 是在初始化的时候就拿到了它
 所有的 view controller,⽽而对于 navigation
 viewcontroller ,只有它的 root view controller 是保
 证⼀一直在堆栈中的
• 当 UIViewController 被压⼊入 stack 时,它的 view 是
 从右边滑⼊入屏幕;当堆栈弹出的时候,顶层的
 view controller 被移出堆栈,它的 view 是从左边
 滑⼊入屏幕的
堆栈




• 这是⼀一个带有两个 view controller 的 navigation
 controller:⼀一个 root view controller,⼀一个是在它之上
 的其他 view controller,可⻅见的是上⾯面的 view controller
viewControllers 和 topViewController
• 和 UITabBarController 类似,
 UINavigationController 有⼀一个 viewControllers 数
 组。它的 root view controller 是数组中的第⼀一个对
 象。当更多的 view controller 被压⼊入堆栈时,他们
 被添加到了这个数组的结尾。这样数组中的最后
 ⼀一个 view controller 就在堆栈的顶部
• UINavigationController 的 topViewController 属性
 保持了⼀一个到堆栈顶部的指针
UINavigationController 的 view
• UINavigationController 是 UIViewController 的⼦子
 类,所以它有⼀一个⾃自⼰己的 view 。它的 view 总是拥
 有两个 subview:
 • ⼀一个是 UINavigationBar
 • 以及 topViewController 的 view
• 也可以把 navigation controller 设成 window 的
 rootViewController 来把它的 view 作为 window 的
 subview
UINavigationController 的 view
升级 Homepwner 应⽤用
• 这⼀一节我们增加⼀一个 UINavigationController 到
 Homepwner 应⽤用并把 ItemsViewController 作为
 UINavigationController 的 rootViewController
• 然后我们创建另外⼀一个可以压⼊入到
 UINavigationController 堆栈的 UIViewController 的
 ⼦子类
升级 Homepwner 应⽤用
• 当⽤用户选择其中⼀一⾏行时,新的 UIViewController 的
 view 将会滑⼊入屏幕。这个 view controller 将会允
 许⽤用户查看和编辑选中的 BNRItem 的属性
• 下⼀一⻚页是更新后的 Homepwner 应⽤用的对象⽰示意图
对象⽰示意图
增加 UINavigationController
      • 修改 HomepwnerAppDelegate.m ⽣生成⼀一个
         UINavigationController,给它⼀一个 root view
         controller,并把它设成 window 的 root view
         controller
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

     ItemsViewController *ivc = [[ItemsViewController alloc] init];

    UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:ivc];

//     [[self window] setRootViewController:ivc];
     [[self window] setRootViewController:navController];

     self.window.backgroundColor = [UIColor whiteColor];
     [self.window makeKeyAndVisible];
     return YES;
}
空的导航栏
• 构建并运⾏行我们会发
现界⾯面顶部多了⼀一个
空⽩白的navigation
bar,同时
ItemsViewController 的
view 被⾃自动调整⼤大⼩小
以适应屏幕
再加⼀一个 UIViewController
• 添加完了 UINavigationController 之后,我们来添
 加⽤用来压⼊入 navigation controller 堆栈的另外⼀一个
 UIViewController

• 创建⼀一个新的 UIViewController ⼦子类,命名为:
 DetailViewController, 勾选上“With XIB for user
 interface”
• 在 Homepwner 中,我们希望⽤用户能够轻击⼀一个
 item,然后跳到另外⼀一个屏幕.

• 在这个新的屏幕上有能够编辑那个 BNRItem 每⼀一
 个属性的⽂文本字段。

• 这个 view 将会被 DetailViewController 的实例控制
更简便建⽴立连接的⽅方式
• detail view 需要 4 个 subview - 每个针对⼀一个
 BNRItem 实例的实例变量
• 因为我们需要在运⾏行时访问这些 subview,
 DetailViewController 需要这些 subviews 的
 outlets。这样我们需要添加四个新的 outlets 到
 DetailViewController
• 可以使⽤用⼀一种稍微简化的⽅方式合并”声明 outlets,
 再建⽴立连接的⽅方式”
配置 DetailViewController XIB
Control-drag UITextField 到
DetailViewController.h 实例变量区域
在弹出窗⼝口输⼊入变量名
设置完的 DetailViewController.h
#import <UIKit/UIKit.h>

@interface DetailViewController : UIViewController
{
    __weak IBOutlet UITextField *nameField;

     __weak IBOutlet UITextField *serialNumberField;

     __weak IBOutlet UILabel *dateLabel;
     __weak IBOutlet UITextField *valueField;

}


    • 注意 XIB ⽂文件不要有坏连接,否则在 XIB ⽂文件被加
     载时,应⽤用会崩溃。
    • 另外针对 XIB ⽂文件中的每个 UITextField,连接
     delegate 属性到 File’s Owner(从 UITextField
     Control-drag 到 File’s Owner, 然后从列表中选择
     delegate)
使⽤用 NavigationController 导航
• 现在我们已经有了 ⼀一个 navigation controller,以
 及两个 view controller 的⼦子类
 ( ItemViewController 和 DetailViewController),
 下⾯面要把它们连起来⼯工作

• 我们要让⽤用户点击 ItemViewController 的 table
 view 的⼀一⾏行,然后 DetailViewController 的 view 就
 可以滑动到屏幕,并且显⽰示选中的 BNRItem 实例
 的属性
压⼊入视图控制器
• ⾸首先我们需要创建⼀一个 DetailViewController 的实
 例,那么这个对象应该在哪⾥里被创建呢?
• 前⾯面的⼏几节我们都是在
 application:didFinishLaunchingWithOptions: ⽅方法
 中实例化我么需要的所有的 controller
• 例如在tab bar controller ⼀一节,我们创建了两个
 view controller,然后⻢马上把它们加到了 tab bar
 controller 的 viewControllers 数组

• 在使⽤用 navigation controller 时,我们不能简单的
 把所有可能⽤用到的 view controller 都存到 stack 中
• navigation controller 的数组是动态的,我们开始
 于⼀一个 root view controller,然后根据⽤用户的需要
 添加 view controller。

• 这样navigation controller 之外的⼀一些对象就需要
 创建 DetailViewController 的实例,并且负责把它
 加⼊入到堆栈中
• 这个对象必须满⾜足两个要求:
• 它需要知道什么时候把 DetailViewController 压⼊入堆栈
• 它需要⼀一个指向 navigation controller 的指针来给
 navigation controller 发送⼀一个名为:
 pushViewController:animated: 的消息
• ItemsViewController 这两个条件都满⾜足:
• 第⼀一,它知道table view 中的⾏行什么时候被轻击,作
 为 table view 的 delegate,当这个事件发⽣生时,它会
 收到 tableView:didSelectRowAtIndexPath: 消息

• 第⼆二,在 navigation controller 堆栈中的 view
 controller 能够通过给它⾃自⼰己发送
 navigationController 消息得到⼀一个指向这个
 navigation controller 的指针

• 作为 root view controller,ItemsViewController 总是
 位于 navigation controller 堆栈中,因此也总是可以
 拿到这个指针
navigationController 属性
tableView:didSelectRowAtIndexPath:
• 这样就由 ItemsViewController 负责⽣生成
 DetailViewController 的实例,并把它加⼊入到堆栈
• ⾸首先在 ItemsViewController.h 中导⼊入
 DetailViewController 的头⽂文件
• 当⼀一⾏行被轻击时,它的 delegate 被发送
 tableView:didSelectRowAtIndexPath: 消息,这个消
 息包含选中⾏行的 index path
• 我们在 ItemsViewController.m 中实现这个⽅方法来
 ⽣生成 DetailViewController 然后把它压⼊入
 navigation controller 的堆栈
ItemsViewController 中的
didSelectRowAtIndexPath:
#import   "ItemsViewController.h"
#import   "BNRItemStore.h"
#import   "BNRItem.h"
#import   "DetailViewController.h"

@implementation ItemsViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath
{
    DetailViewController *detailViewController = [[DetailViewController
alloc] init];

    // 压⼊入 navigation controller stack 的顶部
    [[self navigationController] pushViewController:detailViewController
animated:YES];
}
栈内 view controller 的⽣生命周期
• 因为 UINavigationController 的堆栈是⼀一个数组,
 它将会拥有所有添加到它⾥里⾯面的 view controller 的
 所有权。
• 这样在 tableView:didSelectRowAtIndexPath: 结束
 之后(⽅方法对它的持有消失),
 DetailViewController 只会被 UINavigationController
 所拥有。
• 当堆栈弹出时,DetailViewController 被销毁;当
 下⼀一次⼀一⾏行被轻击时,⼀一个新的
 DetailViewController 的实例被⽣生成
push view controller 的⼀一般模式
• 让⼀一个 view controller 压⼊入新的 view controller 是
 ⼀一种常⻅见的模式。
• ⼀一般是由 root view controller 创建下⼀一个 view
 controller,然后这个 view controller 在这之后再创
 建下⼀一个 view controller...
• 我么也可以让 view controller 能够压⼊入不同类型的
 view controller。

• ⽐比如 Photos 应⽤用,根据选中的媒体类型的不同,
 可以往 navigation controller 的堆栈中分别压⼊入
 video view controller 或者 image view controller
在 view controller 之间传送数据
• ⺫⽬目前屏幕上的 UITextField 都是空的。要填充这些
 字段,我们需要⼀一种⽅方法来把选中的 BNRItem 从
 ItemsViewController 中传到 DetailViewController

• 要成功实现此功能,我们需要给
 DetailViewController ⼀一个属性来持有 BNRItem。
 当⼀一⾏行被轻击时,ItemsViewController 将提供相
 应的的 BNRItem 给被压⼊入堆栈的
 DetailViewController 的实例。
• 这个 DetailViewController 将把这个 BNRItem 的属
 性填充到它的⽂文本字段。

• 在 DetailViewController 的 view 上编辑 UITextFields
 中的⽂文本将改变 BNRItem 的属性
BNRItem *item;
    • 在 DetailViewController.h 中我们增加⼀一个这个属
     性
    • 同时在⽂文件顶部,前置声明 BNRItem
#import <UIKit/UIKit.h>

@class BNRItem;

@interface DetailViewController : UIViewController
{
    __weak IBOutlet UITextField *nameField;

     __weak IBOutlet UITextField *serialNumberField;

     __weak IBOutlet UILabel *dateLabel;
     __weak IBOutlet UITextField *valueField;

}

@property (nonatomic, strong) BNRItem *item;

@end
• 在 DetailViewController.m 中为 item sythesize
   accessor,并且导⼊入 BNRItem 头⽂文件


#import "DetailViewController.h"
#import "BNRItem.h"

@interface DetailViewController ()

@end

@implementation DetailViewController

@synthesize item;
item 内容的显⽰示
    • 当 DetailViewController 的 view 显⽰示在屏幕上时,
       它需要设置它的 subview 来显⽰示 item 的属性
    • 这样我们可以在 DetailViewController.m 中重写
       viewWillAppear: 来给各种 UITextFields 传递 item 的
       属性
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [nameField setText:[item itemName]];
    [serialNumberField setText:[item serialNumber]];
    [valueField setText:[NSString stringWithFormat:@"%d", [item valueInDollars]]];

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    [dateFormatter setTimeStyle:NSDateFormatterNoStyle];

    [dateLabel setText:[dateFormatter stringFromDate:[item dateCreated]]];
}
压⼊入堆栈前给 item 赋值
 • 下⾯面我们在 ItemViewController.m 中的
    tableView:didSelectRowAtIndexPath: 中增加⼀一段代
    码,压⼊入堆栈前把选中的 BNRItem 传给
    DetailViewController,这样 DetailViewController
    就能在 viewWillAppear: 被调⽤用前拿到它的 item
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath
{
    DetailViewController *detailViewController = [[DetailViewController
alloc] init];

   NSArray *items = [[BNRItemStore defaultStore] allItems];
   BNRItem *selectedItem = [items objectAtIndex:[indexPath row]];

    [detailViewController setItem:selectedItem];

    // 压⼊入 navigation controller stack 的顶部
    [[self navigationController] pushViewController:detailViewController
animated:YES];
}
view controller 间传递数据的⽅方法
• 在 view controller 之间传递数据:把所有的数据都
 放到 root view controller,然后传递这些数据的
 ⼀一个⼦子集给下⼀一个 UIViewController,是执⾏行这
 类任务⽐比较简洁⽽而且⾼高效的⽅方法
• 构建运⾏行,然后选择 UITableView 中的⼀一⾏行,出现
 的视图将包含选中的 BNRItem 的信息。但是现在
 我们编辑这些数据的时候,UITableView 并不会反
 映出这些修改

• 我们需要实现⼀一些代码在 BNRItem 被编辑时来更
 新它的属性

• 这些代码放到什么地⽅方呢?
视图的出现和消失
• 在 UINavigationController 将要切换 view 时,它会
 发出两个消息:
 • viewWillDisappear:
 • viewWillAppear:
• 将被弹出堆栈的 UIViewController 会被发送
 viewWillDisappear: 消息
• 将要被置顶到堆栈的 UIViewController 被发送
 viewWillAppear: 消息
• 我们可以在 DetailViewController 被弹出堆栈时,
 把它的 item 的属性设置成 UITextFields 的内容
viewWillDisappear: 及超类
    • 当实现这些针对 view 的 appearing 和
     disappearing 的⽅方法时,⾮非常重要的⼀一点是要调
     ⽤用他们超类的实现 - 这些超类⾥里⾯面也有⼀一些⼯工作
     要做。我们在 DetailViewController.m 中实现
     viewWillDisappear:

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

     // clear first responder
     [[self view] endEditing:YES];
     // 保存变更到 item
     [item setItemName:[nameField text]];
     [item setSerialNumber:[serialNumberField text]];
     [item setValueInDollars:[[valueField text] intValue]];
}
endEditing:
• 注意 endEditing: 的使⽤用。当 endEditing: 消息被发
 送给⼀一个 view,如果它或者它的任何 subview 当
 前是 first responder,它将放弃它的 first responder
 状态,并且键盘将被释放。
• 传递给它的参数决定了 first responder 是否应该被
 强制解除。有些 first responder 可能会拒绝放弃,
 传递 YES 将会忽略这个拒绝。
 [super viewWillDisappear:animated];

 // clear first responder
 [[self view] endEditing:YES];
 // 保存变更到 item
 [item setItemName:[nameField text]];
 [item setSerialNumber:[serialNumberField text]];
 [item setValueInDollars:[[valueField text] intValue]];
viewWillAppear: 和 reloadData:
 • 现在当⽤用户在 UINavigationBar 上轻击 Back 按钮的
    时候,BNRItem 的值将被更新。
 • 当 ItemViewController 重新出现在屏幕上时,它将
    被发送 viewWillAppear: 消息。抓住这个机会来重
    新加载 UITableView 这样⽤用户就可以⽴立即看到这些
    变更。在 ItemViewController.m 中重写
    viewWillAppear:
 • 构建并运⾏行,切换和数据变更都⾮非常流畅了
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [[self tableView] reloadData];
}
UINavigationBar
• UINavigationBar 应该显⽰示当前在
 UINavigationController 堆栈顶部的
 UIViewController 的标题

• 每个 UIViewController 都有⼀一个 UINavigationItem
 类型的 navigationItem 属性。但是不像
 UINavigationBar,UINavigationItem 不是 UIView
 的⼀一个⼦子类,所以它不能出现在屏幕上。
 navigation item 提供了navigation bar 它需要绘制
 的内容
• 当⼀一个 UIViewController 到达
 UINavigationController 堆栈的顶部时,
 UINavigationBar 使⽤用 UIViewController 的
 navigationItem 来配置它⾃自⼰己。

• 如下图所⽰示:
UINavigationItem
UINavigationItem 的 title 字符串
• 默认情况下,UINavigationItem 是空的。从最基本
 的层⾯面上来讲,⼀一个 UINavigationItem 应该有⼀一
 个简单的 title 字符串。
• 当 UIViewController 被移到 navigation 堆栈的顶部
 并且它的 navigationItem 的 title 属性有⼀一个有效
 的字符串,navigation bar 将显⽰示这个字符串。
• 如下图所⽰示:
带 title 的 UINavigationItem
设置 ItemViewController 的 title
    • 在 ItemViewController.m 中修改 init 来设置
     navigationItem 的 title 以显⽰示 Homepwner

- (id)init
{
    self = [super initWithStyle:UITableViewStyleGrouped];
    if (self) {
        UINavigationItem *n = [self navigationItem];
        [n setTitle:@"Homepwner"];

     }
     return self;
}


• 构建并运⾏行我们注意到在 navigation bar 上的
    Homepwner 字符串。但是点击⼀一⾏行以后 title 不在
    了,我们需要也给 DetailViewController ⼀一个 title。
实现 setItem: 并设置 title
 • 我们要把 DetailViewController 的 navigation item
   的 title 设置成 BNRItem 显⽰示的名称⼀一致。
 • 但是我们不能在 DetailViewController 的 init 中设
   置,因为这时候还不知道它的 item 会是什么
 • 我们要在 DetailViewController 设置它的 item 属性
   的时候设置它的 title。在 DetailViewController.m 中
   实现 setItem: 以替换 item 的synthesized 默认的
   setter ⽅方法
- (void)setItem:(BNRItem *)i
{
    item = i;
    [[self navigationItem] setTitle:[item itemName]];
}
UINavigationItem 的三个区域
• 这时候再构建并运⾏行,⽣生成并点击⼀一⾏行的时候我
 们就可以在导航栏上看到选中的 BNRItem 的名称
UINavigationItem 的三个区域
• ⼀一个 navigation item 可以持有不仅只是⼀一个 title
 字符串,如后⾯面的图所⽰示,每个 UINavigationItem
 有三个可⾃自定义的区域:
 • ⼀一个 leftBarButtonItem
 • ⼀一个 rightBarButtonItem
 • 和⼀一个 titleView
• 左右导航按钮条⺫⽬目被指向 UIBarButtonItem 的实
 例,包含的是针对仅可以在 UINavigationBar 或
 UIToolbar 上按显⽰示的⼀一个按钮的信息
包含更多内容的 UINavigationItem
容器的概念
• 和 UINavigationItem ⼀一样,UIBarButtonItem 也不
 是 UIView 的⼀一个⼦子类,也只是提供了⼀一个
 UINavigationBar 需要绘制的内容
• 可以认为 UINavigationItem 和 UIBarButtonItem 是
 字符串,图⽚片以及其他内容的容器。
• ⼀一个 UINavigationBar 知道如何在这些容器中进⾏行
 查找并绘制它找到的内容
titleView
• UINavigationItem 的第三个可绘制的区域是它的
 titleView。我们可以使⽤用⼀一个基本字符串作为它的
 title 或者把⼀一个 UIView 的⼦子类放到 navigation
 item 的正中。但是我们不能两个都设置。
• 如果给⼀一个特定的 view controller 设置⼀一个⾃自定义
 的view(像按钮,滑块,图⽚片,甚⾄至⼀一个地图)
 适合上下⽂文,我们就可以把 navigation item 的
 titleView 设成这个⾃自定义的 view。⼀一般情况下使
 ⽤用⼀一个 title 字符串就⾜足够了。

• 前⼀一个图是就是⼀一个使⽤用⾃自定义视图作为
 titleView 的例⼦子
UIBarButtonItem 的 target-action
• 我们来给 UINavigationBar 添加⼀一个
 UIBarButtonItem。
• 我们希望这个 UIBarButtonItem 当
 ItemsViewController 在堆栈顶部时出现在导航栏
 的右侧。
• 轻击它的时候,它将往列表中添加⼀一个新的
 BNRItem。
• bar button item 具有⼀一个 target-action 对,作⽤用类
 似 UIControl 的 target-action 机制:当被轻击时,
 它发送 action 消息到它的 target。

• 当我们在⼀一个 XIB ⽂文件中设置 target-action 对时,
 通过从按钮 Control-drag 到它的 target,然后从
 IBActions 列表中选择⼀一个⽅方法。
编程设置 target 和 action
• 要编程设置 target-action 对,我们把 target 和
 action 传递给按钮
• 我们在 ItemsViewController.m 中⽣生成⼀一个
 UIBarButtonItem 的实例并且把它的 target 和
 action 传递给它
修改 ItemsViewController.m 中的init
- (id)init
{
    self = [super initWithStyle:UITableViewStyleGrouped];
    if (self) {
        UINavigationItem *n = [self navigationItem];
        [n setTitle:@"Homepwner"];
        // 创建⼀一个会发送 addNewItem: 到ItemsViewController 的导航栏按钮
        UIBarButtonItem *bbi = [[UIBarButtonItem alloc]

initWithBarButtonSystemItem:UIBarButtonSystemItemAdd

target:self

action:@selector(addNewItem:)];
        // 设成右边
        [[self navigationItem] setRightBarButtonItem:bbi];

    }
    return self;
}

    • 构建并运⾏行,点击+按钮,表格中将出现⼀一个新⾏行
    • 令,还有其他的 initialization 消息可以发送给
    UIBarButtonItem 的实例
加⼀一个左按钮
 • 现在我们添加另⼀一个 UIBarButtonItem 来替换掉表
    格头部中 Edit 按钮。在 ItemViewController.m 中继
    续修改 init ⽅方法
- (id)init
{
    self = [super initWithStyle:UITableViewStyleGrouped];
    if (self) {
        UINavigationItem *n = [self navigationItem];
        [n setTitle:@"Homepwner"];

       UIBarButtonItem *bbi = [[UIBarButtonItem alloc]

initWithBarButtonSystemItem:UIBarButtonSystemItemAdd

target:self

action:@selector(addNewItem:)];
        [[self navigationItem] setRightBarButtonItem:bbi];
        // 加⼀一个编辑按钮
        [[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];
    }
    return self;
}
editButtonItem:
   [[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];


• 在 navigation bar 中获得⼀一个编辑按钮只需要⼀一⾏行
 代码
• editButtonItem 是从哪⾥里来的呢?
• UIViewController 有⼀一个 editButtonItem 属性,⽽而
 且当被发送 editButtonItem 时,view controller 就
 ⽣生成⼀一个标题为“Edit”的 UIBarButtonItem。

• 更给⼒力的是,这个按钮⾃自带⼀一个 target-action
 对:当轻击时,它发送 setEditing:animated: 消息
 给它的 UIViewController。

• 构建并运⾏行确认⼀一下 navigation bar 的效果
删除 header view 相关代码
• 现在 Homepwner 已经有了⼀一个全功能的导航栏,可以
 把项⺫⽬目中有关 header view 及其相关的代码都删除掉了
 • ItemsViewController.m 中的
  • tableView:viewForHeaderInSection:
  • tableView:heightForHeaderInSection:
  • headerView:
  • toggleEditingMode:
 • ItemsViewController.h 中的
  • IBOutlet UIView *headerView;
  • - (UIView *)headerView;
  • - (IBAction)toggleEditingMode:
 • 还可以把 HeaderView.xib ⽂文件从 project navigator 中删除
带导航栏的 Homepwner

Más contenido relacionado

La actualidad más candente (9)

I os 09
I os 09I os 09
I os 09
 
10 Editing UITableView
10 Editing UITableView10 Editing UITableView
10 Editing UITableView
 
06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollView06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollView
 
08 Notification and Rotation
08 Notification and Rotation08 Notification and Rotation
08 Notification and Rotation
 
12 Camera
12 Camera12 Camera
12 Camera
 
YUI is Sexy - 使用 YUI 作為開發基礎
YUI is Sexy - 使用 YUI 作為開發基礎YUI is Sexy - 使用 YUI 作為開發基礎
YUI is Sexy - 使用 YUI 作為開發基礎
 
Asp.net開發要注意的是?
Asp.net開發要注意的是?Asp.net開發要注意的是?
Asp.net開發要注意的是?
 
15 Subclassing UITableViewCell
15 Subclassing UITableViewCell15 Subclassing UITableViewCell
15 Subclassing UITableViewCell
 
YUI is Sexy (for JSDC.tw)
YUI is Sexy (for JSDC.tw)YUI is Sexy (for JSDC.tw)
YUI is Sexy (for JSDC.tw)
 

Destacado

Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分
Tom Fan
 
AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状
Tom Fan
 
Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发
Tom Fan
 
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型
Tom Fan
 
Global strategy october 2012
Global strategy october 2012Global strategy october 2012
Global strategy october 2012
Ajibola Alfred
 
Finishing strong 5 mental toughness
Finishing strong   5 mental toughnessFinishing strong   5 mental toughness
Finishing strong 5 mental toughness
Brian Salisbury
 
Finishing strong 6 faith
Finishing strong   6 faithFinishing strong   6 faith
Finishing strong 6 faith
Brian Salisbury
 

Destacado (19)

04 Delegation and Core Location
04 Delegation and Core Location04 Delegation and Core Location
04 Delegation and Core Location
 
01 A Simple iOS Application
01 A Simple iOS Application01 A Simple iOS Application
01 A Simple iOS Application
 
16 CoreData
16 CoreData16 CoreData
16 CoreData
 
03 Managing Memory with ARC
03 Managing Memory with ARC03 Managing Memory with ARC
03 Managing Memory with ARC
 
17 Localization
17 Localization17 Localization
17 Localization
 
14 Saving Loading and Application States
14 Saving Loading and Application States14 Saving Loading and Application States
14 Saving Loading and Application States
 
Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分
 
AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状
 
05 MapKit and Text Input
05 MapKit and Text Input05 MapKit and Text Input
05 MapKit and Text Input
 
02 Objective-C
02 Objective-C02 Objective-C
02 Objective-C
 
Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发
 
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型
 
P3.2. ALIFRUT S.A.
P3.2. ALIFRUT S.A.P3.2. ALIFRUT S.A.
P3.2. ALIFRUT S.A.
 
Writing Frameworks for Fun and Profit
Writing Frameworks for Fun and ProfitWriting Frameworks for Fun and Profit
Writing Frameworks for Fun and Profit
 
Web and Native in 2012
Web and Native in 2012Web and Native in 2012
Web and Native in 2012
 
Adoption cycle
Adoption cycleAdoption cycle
Adoption cycle
 
Global strategy october 2012
Global strategy october 2012Global strategy october 2012
Global strategy october 2012
 
Finishing strong 5 mental toughness
Finishing strong   5 mental toughnessFinishing strong   5 mental toughness
Finishing strong 5 mental toughness
 
Finishing strong 6 faith
Finishing strong   6 faithFinishing strong   6 faith
Finishing strong 6 faith
 

Similar a 11 UINavigationController

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
 
Silverlight 2.0 完全新手學堂,基礎入門 10 大招
Silverlight 2.0 完全新手學堂,基礎入門 10 大招Silverlight 2.0 完全新手學堂,基礎入門 10 大招
Silverlight 2.0 完全新手學堂,基礎入門 10 大招
Chui-Wen Chiu
 
YUIconf2010介绍
YUIconf2010介绍YUIconf2010介绍
YUIconf2010介绍
ling yu
 

Similar a 11 UINavigationController (20)

005
005005
005
 
105-2 iOS程式設計(六)
105-2 iOS程式設計(六)105-2 iOS程式設計(六)
105-2 iOS程式設計(六)
 
I os 05
I os 05I os 05
I os 05
 
I os 02
I os 02I os 02
I os 02
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001
 
UIKit-Swift
UIKit-SwiftUIKit-Swift
UIKit-Swift
 
ASP.NET Core 2.1設計新思維與新發展
ASP.NET  Core 2.1設計新思維與新發展ASP.NET  Core 2.1設計新思維與新發展
ASP.NET Core 2.1設計新思維與新發展
 
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 ...
 
Ios
IosIos
Ios
 
I os 14
I os 14I os 14
I os 14
 
Core data lightweight_migration
Core data lightweight_migrationCore data lightweight_migration
Core data lightweight_migration
 
Silverlight 2.0 完全新手學堂,基礎入門 10 大招
Silverlight 2.0 完全新手學堂,基礎入門 10 大招Silverlight 2.0 完全新手學堂,基礎入門 10 大招
Silverlight 2.0 完全新手學堂,基礎入門 10 大招
 
Uliweb cheat sheet_0.1
Uliweb cheat sheet_0.1Uliweb cheat sheet_0.1
Uliweb cheat sheet_0.1
 
前端MVC之backbone
前端MVC之backbone前端MVC之backbone
前端MVC之backbone
 
I os 08
I os 08I os 08
I os 08
 
[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式
[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式
[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式
 
ASP.NET Core MVC 2.2從開發到測試 - Development & Unit Testing
ASP.NET Core MVC 2.2從開發到測試 - Development & Unit TestingASP.NET Core MVC 2.2從開發到測試 - Development & Unit Testing
ASP.NET Core MVC 2.2從開發到測試 - Development & Unit Testing
 
React + mobx分享(黄英杰)
React + mobx分享(黄英杰)React + mobx分享(黄英杰)
React + mobx分享(黄英杰)
 
Html5移动网站开发实践
Html5移动网站开发实践Html5移动网站开发实践
Html5移动网站开发实践
 
YUIconf2010介绍
YUIconf2010介绍YUIconf2010介绍
YUIconf2010介绍
 

Más de Tom Fan

PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统
Tom Fan
 
HTML5 Web workers
HTML5 Web workersHTML5 Web workers
HTML5 Web workers
Tom Fan
 
Web sockets
Web socketsWeb sockets
Web sockets
Tom Fan
 
Semantics
SemanticsSemantics
Semantics
Tom Fan
 
Multimedia
MultimediaMultimedia
Multimedia
Tom Fan
 
Intro to-html5
Intro to-html5Intro to-html5
Intro to-html5
Tom Fan
 
Html5 history
Html5 historyHtml5 history
Html5 history
Tom Fan
 
Geolocation
GeolocationGeolocation
Geolocation
Tom Fan
 
File api
File apiFile api
File api
Tom Fan
 
Deviceaccess
DeviceaccessDeviceaccess
Deviceaccess
Tom Fan
 
Webstorage
WebstorageWebstorage
Webstorage
Tom Fan
 
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发
Tom Fan
 

Más de Tom Fan (15)

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
 
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发
 
18 NSUserDefaults
18 NSUserDefaults18 NSUserDefaults
18 NSUserDefaults
 

11 UINavigationController

  • 2. • 前⾯面我们看到了 UITabBarController 能够允许⽤用户访问不 同的 screen,标签栏控件适⽤用于⼏几个 screen 之间互不依 赖的情况 • 如果我们想让⽤用户在⼀一些相关联的 screen 之间移动,我 们就可以使⽤用 UINavigationController • ⽐比如 iOS 的 “设置”应⽤用有多个相关的 screen:⼀一系列的 设置和针对每个设置的详细⻚页⾯面,对于每个详细项还有 ⼀一个选项⻚页⾯面,这种类型的界⾯面称作:drill-down interface • 这⼀一个主题我们就使⽤用 UINavigationController 给 Homepwner 添加⼀一个 drill-down interface, 让⽤用户能够查 看和编辑⼀一个 BNRItem 的详细信息
  • 5. • 当应⽤用程序呈现多屏信息时, UINavigationController 维护了⼀一个这些屏幕的堆 栈(stack)。 • 每⼀一个 screen 是 UIViewController 的 view,这个 stack 就是 view controller 的数组。 • 当⼀一个 UIViewController 位于 stack 的顶部时,它 的 view 就可⻅见。
  • 6. • 在初始化⼀一个 UINavigationController 的实例时, 我们给它⼀一个 UIViewController。这个 UIViewController 是 navigation controller 的 root view controller。 • 这个 root view controller 总是在堆栈的底部。应⽤用 程序运⾏行时,可以往堆栈中压⼊入更多的 view controller.
  • 7. • UITabBarController 是在初始化的时候就拿到了它 所有的 view controller,⽽而对于 navigation viewcontroller ,只有它的 root view controller 是保 证⼀一直在堆栈中的 • 当 UIViewController 被压⼊入 stack 时,它的 view 是 从右边滑⼊入屏幕;当堆栈弹出的时候,顶层的 view controller 被移出堆栈,它的 view 是从左边 滑⼊入屏幕的
  • 8. 堆栈 • 这是⼀一个带有两个 view controller 的 navigation controller:⼀一个 root view controller,⼀一个是在它之上 的其他 view controller,可⻅见的是上⾯面的 view controller
  • 9. viewControllers 和 topViewController • 和 UITabBarController 类似, UINavigationController 有⼀一个 viewControllers 数 组。它的 root view controller 是数组中的第⼀一个对 象。当更多的 view controller 被压⼊入堆栈时,他们 被添加到了这个数组的结尾。这样数组中的最后 ⼀一个 view controller 就在堆栈的顶部 • UINavigationController 的 topViewController 属性 保持了⼀一个到堆栈顶部的指针
  • 10. UINavigationController 的 view • UINavigationController 是 UIViewController 的⼦子 类,所以它有⼀一个⾃自⼰己的 view 。它的 view 总是拥 有两个 subview: • ⼀一个是 UINavigationBar • 以及 topViewController 的 view • 也可以把 navigation controller 设成 window 的 rootViewController 来把它的 view 作为 window 的 subview
  • 12. 升级 Homepwner 应⽤用 • 这⼀一节我们增加⼀一个 UINavigationController 到 Homepwner 应⽤用并把 ItemsViewController 作为 UINavigationController 的 rootViewController • 然后我们创建另外⼀一个可以压⼊入到 UINavigationController 堆栈的 UIViewController 的 ⼦子类
  • 13. 升级 Homepwner 应⽤用 • 当⽤用户选择其中⼀一⾏行时,新的 UIViewController 的 view 将会滑⼊入屏幕。这个 view controller 将会允 许⽤用户查看和编辑选中的 BNRItem 的属性 • 下⼀一⻚页是更新后的 Homepwner 应⽤用的对象⽰示意图
  • 15. 增加 UINavigationController • 修改 HomepwnerAppDelegate.m ⽣生成⼀一个 UINavigationController,给它⼀一个 root view controller,并把它设成 window 的 root view controller - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; ItemsViewController *ivc = [[ItemsViewController alloc] init]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:ivc]; // [[self window] setRootViewController:ivc]; [[self window] setRootViewController:navController]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; }
  • 18. • 添加完了 UINavigationController 之后,我们来添 加⽤用来压⼊入 navigation controller 堆栈的另外⼀一个 UIViewController • 创建⼀一个新的 UIViewController ⼦子类,命名为: DetailViewController, 勾选上“With XIB for user interface”
  • 19. • 在 Homepwner 中,我们希望⽤用户能够轻击⼀一个 item,然后跳到另外⼀一个屏幕. • 在这个新的屏幕上有能够编辑那个 BNRItem 每⼀一 个属性的⽂文本字段。 • 这个 view 将会被 DetailViewController 的实例控制
  • 20. 更简便建⽴立连接的⽅方式 • detail view 需要 4 个 subview - 每个针对⼀一个 BNRItem 实例的实例变量 • 因为我们需要在运⾏行时访问这些 subview, DetailViewController 需要这些 subviews 的 outlets。这样我们需要添加四个新的 outlets 到 DetailViewController • 可以使⽤用⼀一种稍微简化的⽅方式合并”声明 outlets, 再建⽴立连接的⽅方式”
  • 24. 设置完的 DetailViewController.h #import <UIKit/UIKit.h> @interface DetailViewController : UIViewController { __weak IBOutlet UITextField *nameField; __weak IBOutlet UITextField *serialNumberField; __weak IBOutlet UILabel *dateLabel; __weak IBOutlet UITextField *valueField; } • 注意 XIB ⽂文件不要有坏连接,否则在 XIB ⽂文件被加 载时,应⽤用会崩溃。 • 另外针对 XIB ⽂文件中的每个 UITextField,连接 delegate 属性到 File’s Owner(从 UITextField Control-drag 到 File’s Owner, 然后从列表中选择 delegate)
  • 26. • 现在我们已经有了 ⼀一个 navigation controller,以 及两个 view controller 的⼦子类 ( ItemViewController 和 DetailViewController), 下⾯面要把它们连起来⼯工作 • 我们要让⽤用户点击 ItemViewController 的 table view 的⼀一⾏行,然后 DetailViewController 的 view 就 可以滑动到屏幕,并且显⽰示选中的 BNRItem 实例 的属性
  • 27. 压⼊入视图控制器 • ⾸首先我们需要创建⼀一个 DetailViewController 的实 例,那么这个对象应该在哪⾥里被创建呢? • 前⾯面的⼏几节我们都是在 application:didFinishLaunchingWithOptions: ⽅方法 中实例化我么需要的所有的 controller
  • 28. • 例如在tab bar controller ⼀一节,我们创建了两个 view controller,然后⻢马上把它们加到了 tab bar controller 的 viewControllers 数组 • 在使⽤用 navigation controller 时,我们不能简单的 把所有可能⽤用到的 view controller 都存到 stack 中
  • 29. • navigation controller 的数组是动态的,我们开始 于⼀一个 root view controller,然后根据⽤用户的需要 添加 view controller。 • 这样navigation controller 之外的⼀一些对象就需要 创建 DetailViewController 的实例,并且负责把它 加⼊入到堆栈中
  • 30. • 这个对象必须满⾜足两个要求: • 它需要知道什么时候把 DetailViewController 压⼊入堆栈 • 它需要⼀一个指向 navigation controller 的指针来给 navigation controller 发送⼀一个名为: pushViewController:animated: 的消息
  • 31. • ItemsViewController 这两个条件都满⾜足: • 第⼀一,它知道table view 中的⾏行什么时候被轻击,作 为 table view 的 delegate,当这个事件发⽣生时,它会 收到 tableView:didSelectRowAtIndexPath: 消息 • 第⼆二,在 navigation controller 堆栈中的 view controller 能够通过给它⾃自⼰己发送 navigationController 消息得到⼀一个指向这个 navigation controller 的指针 • 作为 root view controller,ItemsViewController 总是 位于 navigation controller 堆栈中,因此也总是可以 拿到这个指针
  • 33. tableView:didSelectRowAtIndexPath: • 这样就由 ItemsViewController 负责⽣生成 DetailViewController 的实例,并把它加⼊入到堆栈 • ⾸首先在 ItemsViewController.h 中导⼊入 DetailViewController 的头⽂文件 • 当⼀一⾏行被轻击时,它的 delegate 被发送 tableView:didSelectRowAtIndexPath: 消息,这个消 息包含选中⾏行的 index path • 我们在 ItemsViewController.m 中实现这个⽅方法来 ⽣生成 DetailViewController 然后把它压⼊入 navigation controller 的堆栈
  • 34. ItemsViewController 中的 didSelectRowAtIndexPath: #import "ItemsViewController.h" #import "BNRItemStore.h" #import "BNRItem.h" #import "DetailViewController.h" @implementation ItemsViewController - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath { DetailViewController *detailViewController = [[DetailViewController alloc] init]; // 压⼊入 navigation controller stack 的顶部 [[self navigationController] pushViewController:detailViewController animated:YES]; }
  • 35. 栈内 view controller 的⽣生命周期 • 因为 UINavigationController 的堆栈是⼀一个数组, 它将会拥有所有添加到它⾥里⾯面的 view controller 的 所有权。 • 这样在 tableView:didSelectRowAtIndexPath: 结束 之后(⽅方法对它的持有消失), DetailViewController 只会被 UINavigationController 所拥有。 • 当堆栈弹出时,DetailViewController 被销毁;当 下⼀一次⼀一⾏行被轻击时,⼀一个新的 DetailViewController 的实例被⽣生成
  • 36. push view controller 的⼀一般模式 • 让⼀一个 view controller 压⼊入新的 view controller 是 ⼀一种常⻅见的模式。 • ⼀一般是由 root view controller 创建下⼀一个 view controller,然后这个 view controller 在这之后再创 建下⼀一个 view controller...
  • 37. • 我么也可以让 view controller 能够压⼊入不同类型的 view controller。 • ⽐比如 Photos 应⽤用,根据选中的媒体类型的不同, 可以往 navigation controller 的堆栈中分别压⼊入 video view controller 或者 image view controller
  • 38. 在 view controller 之间传送数据
  • 39. • ⺫⽬目前屏幕上的 UITextField 都是空的。要填充这些 字段,我们需要⼀一种⽅方法来把选中的 BNRItem 从 ItemsViewController 中传到 DetailViewController • 要成功实现此功能,我们需要给 DetailViewController ⼀一个属性来持有 BNRItem。 当⼀一⾏行被轻击时,ItemsViewController 将提供相 应的的 BNRItem 给被压⼊入堆栈的 DetailViewController 的实例。
  • 40. • 这个 DetailViewController 将把这个 BNRItem 的属 性填充到它的⽂文本字段。 • 在 DetailViewController 的 view 上编辑 UITextFields 中的⽂文本将改变 BNRItem 的属性
  • 41. BNRItem *item; • 在 DetailViewController.h 中我们增加⼀一个这个属 性 • 同时在⽂文件顶部,前置声明 BNRItem #import <UIKit/UIKit.h> @class BNRItem; @interface DetailViewController : UIViewController { __weak IBOutlet UITextField *nameField; __weak IBOutlet UITextField *serialNumberField; __weak IBOutlet UILabel *dateLabel; __weak IBOutlet UITextField *valueField; } @property (nonatomic, strong) BNRItem *item; @end
  • 42. • 在 DetailViewController.m 中为 item sythesize accessor,并且导⼊入 BNRItem 头⽂文件 #import "DetailViewController.h" #import "BNRItem.h" @interface DetailViewController () @end @implementation DetailViewController @synthesize item;
  • 43. item 内容的显⽰示 • 当 DetailViewController 的 view 显⽰示在屏幕上时, 它需要设置它的 subview 来显⽰示 item 的属性 • 这样我们可以在 DetailViewController.m 中重写 viewWillAppear: 来给各种 UITextFields 传递 item 的 属性 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [nameField setText:[item itemName]]; [serialNumberField setText:[item serialNumber]]; [valueField setText:[NSString stringWithFormat:@"%d", [item valueInDollars]]]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; [dateFormatter setTimeStyle:NSDateFormatterNoStyle]; [dateLabel setText:[dateFormatter stringFromDate:[item dateCreated]]]; }
  • 44. 压⼊入堆栈前给 item 赋值 • 下⾯面我们在 ItemViewController.m 中的 tableView:didSelectRowAtIndexPath: 中增加⼀一段代 码,压⼊入堆栈前把选中的 BNRItem 传给 DetailViewController,这样 DetailViewController 就能在 viewWillAppear: 被调⽤用前拿到它的 item - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath { DetailViewController *detailViewController = [[DetailViewController alloc] init]; NSArray *items = [[BNRItemStore defaultStore] allItems]; BNRItem *selectedItem = [items objectAtIndex:[indexPath row]]; [detailViewController setItem:selectedItem]; // 压⼊入 navigation controller stack 的顶部 [[self navigationController] pushViewController:detailViewController animated:YES]; }
  • 45. view controller 间传递数据的⽅方法 • 在 view controller 之间传递数据:把所有的数据都 放到 root view controller,然后传递这些数据的 ⼀一个⼦子集给下⼀一个 UIViewController,是执⾏行这 类任务⽐比较简洁⽽而且⾼高效的⽅方法
  • 46. • 构建运⾏行,然后选择 UITableView 中的⼀一⾏行,出现 的视图将包含选中的 BNRItem 的信息。但是现在 我们编辑这些数据的时候,UITableView 并不会反 映出这些修改 • 我们需要实现⼀一些代码在 BNRItem 被编辑时来更 新它的属性 • 这些代码放到什么地⽅方呢?
  • 47. 视图的出现和消失 • 在 UINavigationController 将要切换 view 时,它会 发出两个消息: • viewWillDisappear: • viewWillAppear: • 将被弹出堆栈的 UIViewController 会被发送 viewWillDisappear: 消息 • 将要被置顶到堆栈的 UIViewController 被发送 viewWillAppear: 消息 • 我们可以在 DetailViewController 被弹出堆栈时, 把它的 item 的属性设置成 UITextFields 的内容
  • 48. viewWillDisappear: 及超类 • 当实现这些针对 view 的 appearing 和 disappearing 的⽅方法时,⾮非常重要的⼀一点是要调 ⽤用他们超类的实现 - 这些超类⾥里⾯面也有⼀一些⼯工作 要做。我们在 DetailViewController.m 中实现 viewWillDisappear: - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // clear first responder [[self view] endEditing:YES]; // 保存变更到 item [item setItemName:[nameField text]]; [item setSerialNumber:[serialNumberField text]]; [item setValueInDollars:[[valueField text] intValue]]; }
  • 49. endEditing: • 注意 endEditing: 的使⽤用。当 endEditing: 消息被发 送给⼀一个 view,如果它或者它的任何 subview 当 前是 first responder,它将放弃它的 first responder 状态,并且键盘将被释放。 • 传递给它的参数决定了 first responder 是否应该被 强制解除。有些 first responder 可能会拒绝放弃, 传递 YES 将会忽略这个拒绝。 [super viewWillDisappear:animated]; // clear first responder [[self view] endEditing:YES]; // 保存变更到 item [item setItemName:[nameField text]]; [item setSerialNumber:[serialNumberField text]]; [item setValueInDollars:[[valueField text] intValue]];
  • 50. viewWillAppear: 和 reloadData: • 现在当⽤用户在 UINavigationBar 上轻击 Back 按钮的 时候,BNRItem 的值将被更新。 • 当 ItemViewController 重新出现在屏幕上时,它将 被发送 viewWillAppear: 消息。抓住这个机会来重 新加载 UITableView 这样⽤用户就可以⽴立即看到这些 变更。在 ItemViewController.m 中重写 viewWillAppear: • 构建并运⾏行,切换和数据变更都⾮非常流畅了 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [[self tableView] reloadData]; }
  • 52. • UINavigationBar 应该显⽰示当前在 UINavigationController 堆栈顶部的 UIViewController 的标题 • 每个 UIViewController 都有⼀一个 UINavigationItem 类型的 navigationItem 属性。但是不像 UINavigationBar,UINavigationItem 不是 UIView 的⼀一个⼦子类,所以它不能出现在屏幕上。 navigation item 提供了navigation bar 它需要绘制 的内容
  • 53. • 当⼀一个 UIViewController 到达 UINavigationController 堆栈的顶部时, UINavigationBar 使⽤用 UIViewController 的 navigationItem 来配置它⾃自⼰己。 • 如下图所⽰示:
  • 55. UINavigationItem 的 title 字符串 • 默认情况下,UINavigationItem 是空的。从最基本 的层⾯面上来讲,⼀一个 UINavigationItem 应该有⼀一 个简单的 title 字符串。 • 当 UIViewController 被移到 navigation 堆栈的顶部 并且它的 navigationItem 的 title 属性有⼀一个有效 的字符串,navigation bar 将显⽰示这个字符串。 • 如下图所⽰示:
  • 56. 带 title 的 UINavigationItem
  • 57. 设置 ItemViewController 的 title • 在 ItemViewController.m 中修改 init 来设置 navigationItem 的 title 以显⽰示 Homepwner - (id)init { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { UINavigationItem *n = [self navigationItem]; [n setTitle:@"Homepwner"]; } return self; } • 构建并运⾏行我们注意到在 navigation bar 上的 Homepwner 字符串。但是点击⼀一⾏行以后 title 不在 了,我们需要也给 DetailViewController ⼀一个 title。
  • 58. 实现 setItem: 并设置 title • 我们要把 DetailViewController 的 navigation item 的 title 设置成 BNRItem 显⽰示的名称⼀一致。 • 但是我们不能在 DetailViewController 的 init 中设 置,因为这时候还不知道它的 item 会是什么 • 我们要在 DetailViewController 设置它的 item 属性 的时候设置它的 title。在 DetailViewController.m 中 实现 setItem: 以替换 item 的synthesized 默认的 setter ⽅方法 - (void)setItem:(BNRItem *)i { item = i; [[self navigationItem] setTitle:[item itemName]]; }
  • 60. UINavigationItem 的三个区域 • ⼀一个 navigation item 可以持有不仅只是⼀一个 title 字符串,如后⾯面的图所⽰示,每个 UINavigationItem 有三个可⾃自定义的区域: • ⼀一个 leftBarButtonItem • ⼀一个 rightBarButtonItem • 和⼀一个 titleView • 左右导航按钮条⺫⽬目被指向 UIBarButtonItem 的实 例,包含的是针对仅可以在 UINavigationBar 或 UIToolbar 上按显⽰示的⼀一个按钮的信息
  • 62. 容器的概念 • 和 UINavigationItem ⼀一样,UIBarButtonItem 也不 是 UIView 的⼀一个⼦子类,也只是提供了⼀一个 UINavigationBar 需要绘制的内容 • 可以认为 UINavigationItem 和 UIBarButtonItem 是 字符串,图⽚片以及其他内容的容器。 • ⼀一个 UINavigationBar 知道如何在这些容器中进⾏行 查找并绘制它找到的内容
  • 63. titleView • UINavigationItem 的第三个可绘制的区域是它的 titleView。我们可以使⽤用⼀一个基本字符串作为它的 title 或者把⼀一个 UIView 的⼦子类放到 navigation item 的正中。但是我们不能两个都设置。
  • 64. • 如果给⼀一个特定的 view controller 设置⼀一个⾃自定义 的view(像按钮,滑块,图⽚片,甚⾄至⼀一个地图) 适合上下⽂文,我们就可以把 navigation item 的 titleView 设成这个⾃自定义的 view。⼀一般情况下使 ⽤用⼀一个 title 字符串就⾜足够了。 • 前⼀一个图是就是⼀一个使⽤用⾃自定义视图作为 titleView 的例⼦子
  • 65. UIBarButtonItem 的 target-action • 我们来给 UINavigationBar 添加⼀一个 UIBarButtonItem。 • 我们希望这个 UIBarButtonItem 当 ItemsViewController 在堆栈顶部时出现在导航栏 的右侧。 • 轻击它的时候,它将往列表中添加⼀一个新的 BNRItem。
  • 66. • bar button item 具有⼀一个 target-action 对,作⽤用类 似 UIControl 的 target-action 机制:当被轻击时, 它发送 action 消息到它的 target。 • 当我们在⼀一个 XIB ⽂文件中设置 target-action 对时, 通过从按钮 Control-drag 到它的 target,然后从 IBActions 列表中选择⼀一个⽅方法。
  • 67. 编程设置 target 和 action • 要编程设置 target-action 对,我们把 target 和 action 传递给按钮 • 我们在 ItemsViewController.m 中⽣生成⼀一个 UIBarButtonItem 的实例并且把它的 target 和 action 传递给它
  • 68. 修改 ItemsViewController.m 中的init - (id)init { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { UINavigationItem *n = [self navigationItem]; [n setTitle:@"Homepwner"]; // 创建⼀一个会发送 addNewItem: 到ItemsViewController 的导航栏按钮 UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewItem:)]; // 设成右边 [[self navigationItem] setRightBarButtonItem:bbi]; } return self; } • 构建并运⾏行,点击+按钮,表格中将出现⼀一个新⾏行 • 令,还有其他的 initialization 消息可以发送给 UIBarButtonItem 的实例
  • 69. 加⼀一个左按钮 • 现在我们添加另⼀一个 UIBarButtonItem 来替换掉表 格头部中 Edit 按钮。在 ItemViewController.m 中继 续修改 init ⽅方法 - (id)init { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { UINavigationItem *n = [self navigationItem]; [n setTitle:@"Homepwner"]; UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewItem:)]; [[self navigationItem] setRightBarButtonItem:bbi]; // 加⼀一个编辑按钮 [[self navigationItem] setLeftBarButtonItem:[self editButtonItem]]; } return self; }
  • 70. editButtonItem: [[self navigationItem] setLeftBarButtonItem:[self editButtonItem]]; • 在 navigation bar 中获得⼀一个编辑按钮只需要⼀一⾏行 代码
  • 71. • editButtonItem 是从哪⾥里来的呢? • UIViewController 有⼀一个 editButtonItem 属性,⽽而 且当被发送 editButtonItem 时,view controller 就 ⽣生成⼀一个标题为“Edit”的 UIBarButtonItem。 • 更给⼒力的是,这个按钮⾃自带⼀一个 target-action 对:当轻击时,它发送 setEditing:animated: 消息 给它的 UIViewController。 • 构建并运⾏行确认⼀一下 navigation bar 的效果
  • 72. 删除 header view 相关代码 • 现在 Homepwner 已经有了⼀一个全功能的导航栏,可以 把项⺫⽬目中有关 header view 及其相关的代码都删除掉了 • ItemsViewController.m 中的 • tableView:viewForHeaderInSection: • tableView:heightForHeaderInSection: • headerView: • toggleEditingMode: • ItemsViewController.h 中的 • IBOutlet UIView *headerView; • - (UIView *)headerView; • - (IBAction)toggleEditingMode: • 还可以把 HeaderView.xib ⽂文件从 project navigator 中删除