狠狠撸

狠狠撸Share a Scribd company logo
UIPopoverController 和 Modal View
Controller




范圣刚,princetoad@gmail.com, www.tfan.org
? 截?至??目前我们已经看到了有四种?方法可以?用来显
?示?一个 view controller 的 view:
? 第?一种是可以设成 window 的 root view controller,
 view 就变成了 window 的 subview

? 第?二种是把 view controller 压?入?一个
 UINavigationController 的堆栈

? 第三种是我们可以把它添加到?一个 UITabBarController
? 以及模态化的把它呈现出来
? 在这个主题我们来看?一下 UIPopoverController, 以
 及更多?用来呈现模态化 view controller 的选项

? 有些选项只对 iPad ?生效,我们第?一步先把
 Homepwner 改成 universal appication - 就是可以
 本地化的在iPad,以及iPhone,iPod touch上运?行
Universalizing Homepwner
? Device -> Universal
改进界?面的两种?方式
? 改成 Universal 之后,ItemsViewController 视图在
 iPad 上看起来很好,但是如果选择?一?行的话,可
 以看到 DetailViewController 的界?面可能需要?一些
 加?工(?而且轻击获取图?片的按钮会抛出异常,后
 ?面?马上会纠正这个问题)
? 改进 DetailViewController 界?面效果的?一种?方法是
 设置 autosizing mask,让他的 iPad 上?自动调整来
 适合 iPad

? 另外?一种是创建两个完全独?立的 XIB ?文件,?一个
 针对 iPhone,?一个针对 iPad(加上~ipad 后缀)
 (要在iPad版本上重新创建视图和重新连接)。
背景颜?色
? 现在我们先不关?心 DetailViewController 视图的显
 ?示。我们先来处理?一下 iPhone 下的纯?白背景
? 如果是 iPad 的话,我们把背景?色设成
 groupTableViewBackgroundColor 颜?色;如果是
 iPhone 的话,给它设定?一个和
 ItemsViewController接近的颜?色
确定 device family
 ? ?首先我们要能够判断设备类型
 ? 使?用 UIDevice 类的类?方法 currentDevice 可以得到当前
  设备,然后通过检查它的 userInterfaceIdiom 属性判断
  是何种设备。??目前有两个可能的值:
  ? UIUserInterfaceIdiomPad - iPad
  ? UIUserInterfaceIdiomPhone - iPhone 或 iPod touch
userInterfaceIdiom:
 ? 在 DetailViewController.m 中修改 viewDidLoad:
 ? 我们在后?面会经常?用到这种判断
- (void)viewDidLoad
{
    [super viewDidLoad];
//    [[self view] setBackgroundColor:[UIColor
groupTableViewBackgroundColor]];

    UIColor *clr = nil;
    if ([[UIDevice currentDevice] userInterfaceIdiom] ==
UIUserInterfaceIdiomPad) {
        clr = [UIColor groupTableViewBackgroundColor];
    } else {
        clr = [UIColor colorWithRed:0.875 green:0.88 blue:0.91 alpha:1];
    }
    [[self view] setBackgroundColor:clr];
}
?自动翻转设置
 ? 我们要让应?用在 iPad 上?支持所有?方向的翻转,在
    iPhone 上只?支持 Portrait
 ? 在 ItemsViewController.m 和
    DetailViewController.m 中都增加下?面的?方法
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] ==
UIUserInterfaceIdiomPad) {
        return YES;
    } else {
        return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
    }
}

构建并运?行可以看到在 iPad 上视图可以进?行翻转
UIPopoverController
? 现在 Homepwner 已经可以在 iPad 上运?行了,我们
 可以充分利?用 iPad 特有的?方法来呈现 view controller

? ?首先我们来看?一下 UIPopoverController
? 应?用程序经常需要呈现?一个 view controller 给?用户,
 让?用户选择下?一步做什么。?比如 image picker。

? 在 iPhone 和 iPod touch 上,这类 view controller 通
 常是模态化呈现,占满整个屏幕。

? 有更多屏幕空间的 iPad 有另外?一种选择:
 UIPopoverController
? popover controller 在应?用程序界?面的上?面浮动显
 ?示另?一个 view controller 的 view,显?示在?一个边
 框窗?口中。

? 当我们?生成?一个 UIPopoverController 时,我们把
 这另?一个 view controller 设成了 popover controller
 的 contentViewController

? 我们要实现的是,?用户轻击 camera 按钮时,我们
 在?一个 UIPopoverController ?里?面把
 UIImagePickerController 呈现出来
popover controller
popover controller 声明
    ? 在 DetailViewController 中增加?一个实例变量来持
     有 popover controller;同时声明
     DetailViewController 符合
     UIPopoverControllerDelegate
@interface DetailViewController : UIViewController
<UINavigationControllerDelegate, UIImagePickerControllerDelegate,
UITextFieldDelegate, UIPopoverControllerDelegate>
{
    __weak IBOutlet UITextField *nameField;

     __weak IBOutlet UITextField *serialNumberField;

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

     __weak IBOutlet UIImageView *imageView;

     UIPopoverController *imagePickerPopover;
}
创建,设置和呈现 popover
 ? 如果是 iPad 的话,获取图?片时?用 popover
    controller;在 DetailViewController.m 中,在
    takePicture: 末尾增加下列代码
    [imagePicker setDelegate:self];

    // 把 image picker 放到屏幕上
//    [self presentViewController:imagePicker animated:YES completion:nil];
    if ([[UIDevice currentDevice] userInterfaceIdiom] ==
UIUserInterfaceIdiomPad) {
        // 创建?一个新的将要显?示 imagePicker 的 popover control
        imagePickerPopover = [[UIPopoverController alloc]
initWithContentViewController:imagePicker];

        [imagePickerPopover setDelegate:self];

        [imagePickerPopover presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    } else {
        [self presentViewController:imagePicker animated:YES
completion:nil];
    }
}
释放 popover
? 构建并在 iPad 模拟器中运?行,导航到
 DetailViewController 然后点击 camera 按钮,pop
 over 会出现并且显?示 image picker。选择?一个图
 ?片,会出现在 DetailViewController 的 view 上。
? 我们可以点击屏幕上的任何位置来释放这个
 popover controller。当 popover 被以这种?方式释
 放时,它发送?一个
 popoverControllerDidDismissPopover: 消息给它
 的 delegate
popoverControllerDidDismissPopover:
- (void)popoverControllerDidDismissPopover:(UIPopoverController
*)popoverController
{
    NSLog(@"释放 popover");
    imagePickerPopover = nil;
}


 ? 在 DetailViewController.m 中实现这个?方法
 ? 注意这?里我们把 imagePickerPopover 设成 nil 来销
    毁它,我们每次点击 camera 按钮时都会?生成?一个
    新的
选完图?片后释放 popover
 ? 在DetailViewController.m 中
   imagePickerController:didFinishPickingMediaWithInfo
   : 末尾增加代码来释放 popover
    [imageView setImage:image];

    // 把 image picker 从屏幕上移?走,必须调?用这个 dismiss ?方法
//    [self dismissViewControllerAnimated:YES completion:nil];
    if ([[UIDevice currentDevice] userInterfaceIdiom] ==
UIUserInterfaceIdiomPhone) {
        [self dismissViewControllerAnimated:YES completion:nil];
    } else {
        [imagePickerPopover dismissPopoverAnimated:YES];
        imagePickerPopover = nil;
    }
}

? 因为显式发送 dismissPopoverAnimated: 消息释放时,不会
  发送 popoverControllerDidDismissPopover: 给它的
  delegate,所有这?里要设置 imagePickerPopover 为 nil
销毁不可?见的 popover controller
? 还有?一个?小 bug 要处理,UIPopoverController 可
 ?见的情况下,再次点击 camera 按钮,应?用会崩溃
? 原因是点击 camera 按钮,在 takePicture: 中
 imagePickerPopover 被设成了新的
 UIPopoverController,这时屏幕上的
 UIPopoverController 就被销毁。
? 已经销毁的对象还是可?见的,就出现异常了。我
 们要确保销毁的是不可?见的 UIPopoverController
已有,先释放
 ? 在 DetailViewController.m 中,takePicker: 顶部增
    加下?面的代码,增加对 imagePickerPopover 是否
    可?见的判断
 ? 这就实现了点击 camera,如果当前有显?示
    popover,先释放
- (IBAction)takePicture:(id)sender {
    // 避免多次点击camera按钮引起异常
    if ([imagePickerPopover isPopoverVisible]) {
        [imagePickerPopover dismissPopoverAnimated:YES];
        imagePickerPopover = nil;
        return;
    }

    UIImagePickerController *imagePicker = [[UIImagePickerController alloc]
init];
更多对于模态化 View Controller
? 在这个部分,我们更新 Homepwner,让它在?用户
新建?一个 BNRItem 时模态化的呈现
DetailViewController

? 当?用户选择?一个已经存在的 BNRItem 时,我们和
之前?一样把 DetailViewController 压?入
UINavigationController 的堆栈
新建?一个 BNRItem
双重?用法
 ? 要实现 DetailViewController 的双重?用法(new 或
    edit),我们要给它?一个名为 initForNewItem: 的新
    的 designated initializer
 ? 在这个 initializer 中检查这个实例是被?用来?生成?一
    个新的 BNRItem 还是?用来显?示?一个已经存在的,
    然后对应的来设置?用户界?面
 ? ?首先在 DetailViewController.h 中声明这个
    initializer
- (id)initForNewItem:(BOOL)isNew;

@property (nonatomic, strong) BNRItem *item;
Done 和 Cancel
? 如果 DetailViewController 被?用来?生成?一个新的
 BNRItem,我们希望在它的 navigation item 上显?示
 ?一个 Done 按钮和?一个 Cancel 按钮
? 在 DetailViewController.m 中实现这个?方法
实现 initForNewItem:
- (id)initForNewItem:(BOOL)isNew
{
    self = [super initWithNibName:@"DetailViewController" bundle:nil];

    if (self) {
        if (isNew) {
            UIBarButtonItem *doneItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self
action:@selector(save:)];
            [[self navigationItem] setRightBarButtonItem:doneItem];

            UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self
action:@selector(cancel:)];
            [[self navigationItem] setLeftBarButtonItem:cancelItem];
        }
    }

    return self;
}



    ? 如果是新建的话,设置左右按钮
重写超类 designated initializer
  ? 之前我们更改 designated initializer 的话,会重写
     超类的 initializer 来调?用新的。这?里我们通过抛出
     ?一个异常来使对超类 designated initializer 的调?用
     ?非法
  ? 在 DetailViewController.m 中,重写
     UIViewController 的 designated initializer
 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle
 *)nibBundleOrNil
 {
     @throw [NSException exceptionWithName:@"Wrong initializer" reason:@"Use
 initForNewItem" userInfo:nil];
     return nil;
 }


? NSException。异常时,name 和 reason 会在控制台
 显?示
确认异常
? ??目前在 ItemsViewController 的
 tableView:didSelectRowAtIndexPath: ?方法中调?用
 initWithNibName:bundle: ?方法(对 init ?方法的调?用
 最终会调?用 initWithNibName:bundle :?方法)
? 构建并运?行,在表格中选择?一?行的结果是 “Wrong
 initializer”异常被抛出,程序挂起,可以在控制台
 中看到?一个异常(name 和 reason)
使?用新的 initializer
 ? 为了消除这个异常,我们在
    ItemsViewController.m 中更新
    tableView:didSelectRowAtIndexPath: 以使?用新的
    initializer
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath
{
//    DetailViewController *detailViewController = [[DetailViewController
alloc] init];
    DetailViewController *detailViewController = [[DetailViewController
alloc] initForNewItem:NO];

   NSArray *items = [[BNRItemStore defaultStore] allItems];
? 现在我们已经能让新的 initializer ?工作了,下?面更
 改?一下?用户增加?一个新的 item 时我们的操作

? 在 ItemsViewController.m 中,编辑 addNewItem:
 来在 UINavigationController 中创建?一个
 DetailViewController 的实例,并且把这个
 navigation controller 模态化的呈现
修改 ItemsViewController.m 中addNewItem
- (IBAction)addNewItem:(id)sender
{
    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];
    DetailViewController *detailViewController = [[DetailViewController
alloc] initForNewItem:YES];
    [detailViewController setItem:newItem];

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


     [self presentViewController:navController animated:YES completion:nil];
}

    ? 构建并运?行,DetailViewController 从底部滑出
模态化 view controllers 的释放
? 要释放(dismiss)?一个模态化呈现的 view
 controller,必须给呈现它的 view controller 发送
 dismissViewControllerAnimated:completion: 消息

? 我们在之前的 DetailViewController 中,当
 UIImagePickerController 完成?工作并告诉
 DetailViewController 以后, DetailViewController 就
 把它释放了
? 现在的情况稍有不同。当?一个新的 item 被?生成
 时,ItemsViewController 模态化的呈现这个
 DetailViewController。

? 这个 DetailViewController 在它的 navigationItem
 上有两个按钮:Cancel 和 Done,按钮按下时
 DetailViewController 将被释放
? 问题是,这些按钮的 action message 是发给
 DetailViewController 的,DetailViewController 需
 要?一种?方法来告诉呈现它的并且负责释放它的
 ItemViewController,它已经完?工可以被释放了
presentingViewController 属性
? 幸运的是,每个 UIViewController 都有?一个
 presentingViewController 属性,这个属性指向呈
 现它的 view controller
? DetailViewController 将会获取?一个指向它的
 presentingViewController 的指针,并把
 dismissViewControllerAnimated:completion: 消息
 发送给它
实现 save action ?方法
 ? 在 DetailViewController.m 中实现为 Done 按钮实
   现它的 save action ?方法
- (void)save:(id)sender
{
    [[self presentingViewController] dismissViewControllerAnimated:YES
completion:nil];
}
Cancel -> 删除新建的 item
? Cancel 按钮需要多做?一点?工作。
? ?用户点击 ItemViewController 的按钮添加?一个新的
 item 到列表,就创建了?一个新的 BNRItem 实例,
 添加到 store,然后 DetailViewController 才滑动出
 来供我们编辑
? 如果?用户取消了这个 item 的?生成,那么这个
 BNRItem 就需要从 store 中删除
? 所以我们需要在 Cancel 结束后多做?一些?工作
Cancel 的实现
 ? ?首先要在 DetailViewController.m 中导?入
    BNRItemStore 的头?文件

#import   "DetailViewController.h"
#import   "BNRItem.h"
#import   "BNRImageStore.h"
#import   "BNRItemStore.h"


- (void)cancel:(id)sender
{
    [[BNRItemStore defaultStore] removeItem:item];

    [[self presentingViewController] dismissViewControllerAnimated:YES
completion:nil];
}


? 构建并运?行,新建然后cancel,不会有内容添加到
  table view;新建,然后 Done,
  DetailViewController 将滑出屏幕
action message 的验证
? 注意 cancel: 和 save: ?方法没有在任何地?方做声
 明,这并没有什么问题
? 声明?一个?方法是为了让编译器知道这个?方法的存
 在,在设置 UIBarButtonItem 和 UIControl 的 action
 时,编译器不会验证 action message,因为此时
 并没有调?用。消息是在运?行时实际被发送时才被
 验证
? 如果这个?方法被定义了,将会运?行良好;否则就
 会得到?一个 unrecognized selector exception
真正的 presentingViewController
? UINavigationController
Modal view controller 的样式
? 在 iPhone 和 iPod touch上,?一个模态化 view
 controller 会展满整个屏幕,是默认的?而且也只能
 这样

? 在 iPad 上,我们有两个附加选项:
 ? form sheet style
 ? 和 page sheet style
? 我们可以通过设置modal view controller 的
 modalPresentationStyle 属性为?一个预设的常量:
 ? UIModalPresentationFormSheet
 ? 或 UIModalPresentationPageSheet 来更改这个呈现
form sheet style
? form sheet style 在 iPad
 屏幕中间的?一个矩形
 内显?示模态化 view
 controller,并且把呈
 现它的 view controller
 的 view 变暗
page sheet style
? 在 portrait 模式下和默
 认全屏相同

? 在 landscape 模式下,
 保持它的宽度和
 portrait 模式下相同,
 并且把贴在它下?面的
 呈现它的 view
 controller 的左右边变
 暗
改变 presentation style
 ? 在 ItemsViewController.m 中修改 addNewItem: ?方
   法来变更被呈现的 UINavigationController 的
   presentation style

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

    [navController setModalPresentationStyle:UIModalPresentationFormSheet];

    [self presentViewController:navController animated:YES completion:nil];
新的 item 不?见了?
? 再次构建并运?行,点击按钮增加?一个新的item,
 modal view controller 滑?入屏幕。然后点击 Done
 按钮,table view 重新出来了,但是新的 BNRItem
 并没有显?示出来!??
? 变更 presentation style 前,modal view controller
 是全屏显?示的,这引起了 ItemsViewController 的
 view disappear。

? 当 modal view controller 被 dismiss 后,
 ItemsViewController 被发送 viewWillAppear: 消
 息,然后我们是在 viewWillAppear: 中重新加载所
 有新加的数据
table view 重新加载数据
? 当使?用新的 presentation style 时,
 ItemsViewController 的 view 在它呈现 view
 controller 的时候并没有消失(只是颜?色变暗
 了)。
? 因?而在 modal view controller 被 dismissed 的时候
 没有被发送重新出现的消息,因此也没有机会重
 新加载数据。
? ItemsViewController 重新加载它的 table view 的代
 码很简单:
 ? 类似:[[self tableview] reloadData]
 ? 但是我们必须找到?一个机会来重新加载这些数据
? 我们需要做的是打包上?面的代码,然后恰好在
modal view controller 被 dismissed 的时候让它执
?行

? 我们可以使?用
dismissViewControllerAnimated:completion: 中?一
个内置的机制来完成这个
Completion blocks
? 我们可以把重新加载 table view 的代码放?入?一个
    block,然后传给
    dismissViewControllerAnimated:completion: , 然
    后代码就正好在 modal view controller 被
    dismissed 时候执?行

 ? 在 DetailViewController.h 中,增加?一个指向?一个
    block 的属性,在 .m 中 synthesize

@property (nonatomic, copy) void (^dismissBlock)(void);


@implementation DetailViewController

@synthesize item;
@synthesize dismissBlock;
传递 block
 ? 我们不能在 DetailViewController ?自?身中创建这个
    block,?而只能在 ItemsViewController 中创建,只
    有 ItemsViewController 才知道它的 tableView
 ? 在 ItemsViewController.m 中的 addNewItem: ?方法
    中,?生成?一个重新加载 ItemsViewController 的表
    格的 block,并把它传给 DetailViewController
- (IBAction)addNewItem:(id)sender
{
    BNRItem *newItem = [[BNRItemStore defaultStore] createItem];

    DetailViewController *detailViewController = [[DetailViewController
alloc] initForNewItem:YES];
    [detailViewController setItem:newItem];

    [detailViewController setDismissBlock:^{
        [[self tableView] reloadData];
    }];

    UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:detailViewController];
? 这时当?用户再点击按钮添加?一个新的 item 时,?一
 个重新加载 ItemsViewController 的表格的 block 会
 被创建,并被设成 DetailViewController 的
 dismissBlock。

? DetailViewController 将会持有这个block 直到
 DetailViewController 需要被释放

? 在被释放时,DetailViewController 将把这个 block
 传给 dismissViewControllerAnimated:completion:
? 在 DetailViewController.m 中修改 save: 和 cancel:
    的实现,使?用 dismissBlock 作为?一个参数发送
    dismissViewControllerAnimated:completion: 消息

 ? 再构建并运?行,新增加的 BNRItem 就会出现在表
    格中了
- (void)cancel:(id)sender
{
    [[BNRItemStore defaultStore] removeItem:item];

//    [[self presentingViewController] dismissViewControllerAnimated:YES
completion:nil];
    [[self presentingViewController] dismissViewControllerAnimated:YES
completion:dismissBlock];
}
- (void)save:(id)sender
{
//    [[self presentingViewController] dismissViewControllerAnimated:YES
completion:nil];
    [[self presentingViewController] dismissViewControllerAnimated:YES
completion:dismissBlock];
}
Modal view controller 的转换
? 最后我们看?一下 modal view controller 的
 transitions。

? 除了可以更改 modal view controller 的
 presentation style 之外,我们也可以更改把它放
 置到屏幕上时的动画效果。
? 使?用 modalTransitonStyle 属性,可以设置?一个预
   定义的常量。

 ? 默认是将从屏幕底部滑出到屏幕
 ? 在 ItemsViewController.m 中更新 addItmName: ?方
   法使?用?一个不同的 transition(横向翻动)
    [navController setModalPresentationStyle:UIModalPresentationFormSheet];
    [navController
setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];

    [self presentViewController:navController animated:YES completion:nil];

More Related Content

Viewers also liked (8)

07 View Controllers
07 View Controllers07 View Controllers
07 View Controllers
Tom Fan
?
14 Saving Loading and Application States
14 Saving Loading and Application States14 Saving Loading and Application States
14 Saving Loading and Application States
Tom Fan
?
02 Objective-C
02 Objective-C02 Objective-C
02 Objective-C
Tom Fan
?
Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分
Tom Fan
?
17 Localization
17 Localization17 Localization
17 Localization
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
?
05 MapKit and Text Input
05 MapKit and Text Input05 MapKit and Text Input
05 MapKit and Text Input
Tom Fan
?
07 View Controllers
07 View Controllers07 View Controllers
07 View Controllers
Tom Fan
?
14 Saving Loading and Application States
14 Saving Loading and Application States14 Saving Loading and Application States
14 Saving Loading and Application States
Tom Fan
?
02 Objective-C
02 Objective-C02 Objective-C
02 Objective-C
Tom Fan
?
Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分
Tom Fan
?
17 Localization
17 Localization17 Localization
17 Localization
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
?
05 MapKit and Text Input
05 MapKit and Text Input05 MapKit and Text Input
05 MapKit and Text Input
Tom Fan
?

Similar to 13 UIPopoverController and Modal View Controller (17)

10 Editing UITableView
10 Editing UITableView10 Editing UITableView
10 Editing UITableView
Tom Fan
?
15 Subclassing UITableViewCell
15 Subclassing UITableViewCell15 Subclassing UITableViewCell
15 Subclassing UITableViewCell
Tom Fan
?
Core data lightweight_migration
Core data lightweight_migrationCore data lightweight_migration
Core data lightweight_migration
Michael Pan
?
View Animation
View AnimationView Animation
View Animation
Wing Yiu Ng
?
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
?
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAPiOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
Ming-Sian Lin
?
Uliweb cheat sheet_0.1
Uliweb cheat sheet_0.1Uliweb cheat sheet_0.1
Uliweb cheat sheet_0.1
modou li
?
製作 Unity Plugin for iOS
製作 Unity Plugin for iOS製作 Unity Plugin for iOS
製作 Unity Plugin for iOS
Johnny Sung
?
Wxpython In Action
Wxpython In ActionWxpython In Action
Wxpython In Action
智锋 范
?
React + mobx分享(黄英杰)
React + mobx分享(黄英杰)React + mobx分享(黄英杰)
React + mobx分享(黄英杰)
kkxx1254
?
10 Editing UITableView
10 Editing UITableView10 Editing UITableView
10 Editing UITableView
Tom Fan
?
15 Subclassing UITableViewCell
15 Subclassing UITableViewCell15 Subclassing UITableViewCell
15 Subclassing UITableViewCell
Tom Fan
?
Core data lightweight_migration
Core data lightweight_migrationCore data lightweight_migration
Core data lightweight_migration
Michael Pan
?
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
?
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAPiOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
Ming-Sian Lin
?
Uliweb cheat sheet_0.1
Uliweb cheat sheet_0.1Uliweb cheat sheet_0.1
Uliweb cheat sheet_0.1
modou li
?
製作 Unity Plugin for iOS
製作 Unity Plugin for iOS製作 Unity Plugin for iOS
製作 Unity Plugin for iOS
Johnny Sung
?
Wxpython In Action
Wxpython In ActionWxpython In Action
Wxpython In Action
智锋 范
?
React + mobx分享(黄英杰)
React + mobx分享(黄英杰)React + mobx分享(黄英杰)
React + mobx分享(黄英杰)
kkxx1254
?

More from Tom Fan (17)

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
?
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
?
Deviceaccess
DeviceaccessDeviceaccess
Deviceaccess
Tom Fan
?
Css3
Css3Css3
Css3
Tom Fan
?
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发
Tom Fan
?
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型
Tom Fan
?
18 NSUserDefaults
18 NSUserDefaults18 NSUserDefaults
18 NSUserDefaults
Tom Fan
?
16 CoreData
16 CoreData16 CoreData
16 CoreData
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
?
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
?
Deviceaccess
DeviceaccessDeviceaccess
Deviceaccess
Tom Fan
?
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发
Tom Fan
?
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型
Tom Fan
?
18 NSUserDefaults
18 NSUserDefaults18 NSUserDefaults
18 NSUserDefaults
Tom Fan
?
16 CoreData
16 CoreData16 CoreData
16 CoreData
Tom Fan
?

Recently uploaded (6)

阿伯丁大学毕业证(鲍辞础学位证学历认证国外留学)办理
阿伯丁大学毕业证(鲍辞础学位证学历认证国外留学)办理阿伯丁大学毕业证(鲍辞础学位证学历认证国外留学)办理
阿伯丁大学毕业证(鲍辞础学位证学历认证国外留学)办理
kaozytf
?
2025 NVIDIA GTC: Crack the AI Black Box: Practical Techniques for Explainable AI
2025 NVIDIA GTC: Crack the AI Black Box: Practical Techniques for Explainable AI2025 NVIDIA GTC: Crack the AI Black Box: Practical Techniques for Explainable AI
2025 NVIDIA GTC: Crack the AI Black Box: Practical Techniques for Explainable AI
David vonThenen
?
匹兹堡大学毕业证(笔滨罢罢学位证范本毕业证书)办理
匹兹堡大学毕业证(笔滨罢罢学位证范本毕业证书)办理匹兹堡大学毕业证(笔滨罢罢学位证范本毕业证书)办理
匹兹堡大学毕业证(笔滨罢罢学位证范本毕业证书)办理
kaozytf
?
Build_With_AI_2025 Gemini 2.0 New Function
Build_With_AI_2025  Gemini 2.0 New FunctionBuild_With_AI_2025  Gemini 2.0 New Function
Build_With_AI_2025 Gemini 2.0 New Function
kevinchiu59
?
Cantonmade 2025 Hotel Supplier Catalog: Technical Specs for Engineers & Integ...
Cantonmade 2025 Hotel Supplier Catalog: Technical Specs for Engineers & Integ...Cantonmade 2025 Hotel Supplier Catalog: Technical Specs for Engineers & Integ...
Cantonmade 2025 Hotel Supplier Catalog: Technical Specs for Engineers & Integ...
RayChan91
?
2025 DeveloperWeek - The Sound of Innovation: Why Voice Cloning Will Redefine...
2025 DeveloperWeek - The Sound of Innovation: Why Voice Cloning Will Redefine...2025 DeveloperWeek - The Sound of Innovation: Why Voice Cloning Will Redefine...
2025 DeveloperWeek - The Sound of Innovation: Why Voice Cloning Will Redefine...
David vonThenen
?
阿伯丁大学毕业证(鲍辞础学位证学历认证国外留学)办理
阿伯丁大学毕业证(鲍辞础学位证学历认证国外留学)办理阿伯丁大学毕业证(鲍辞础学位证学历认证国外留学)办理
阿伯丁大学毕业证(鲍辞础学位证学历认证国外留学)办理
kaozytf
?
2025 NVIDIA GTC: Crack the AI Black Box: Practical Techniques for Explainable AI
2025 NVIDIA GTC: Crack the AI Black Box: Practical Techniques for Explainable AI2025 NVIDIA GTC: Crack the AI Black Box: Practical Techniques for Explainable AI
2025 NVIDIA GTC: Crack the AI Black Box: Practical Techniques for Explainable AI
David vonThenen
?
匹兹堡大学毕业证(笔滨罢罢学位证范本毕业证书)办理
匹兹堡大学毕业证(笔滨罢罢学位证范本毕业证书)办理匹兹堡大学毕业证(笔滨罢罢学位证范本毕业证书)办理
匹兹堡大学毕业证(笔滨罢罢学位证范本毕业证书)办理
kaozytf
?
Build_With_AI_2025 Gemini 2.0 New Function
Build_With_AI_2025  Gemini 2.0 New FunctionBuild_With_AI_2025  Gemini 2.0 New Function
Build_With_AI_2025 Gemini 2.0 New Function
kevinchiu59
?
Cantonmade 2025 Hotel Supplier Catalog: Technical Specs for Engineers & Integ...
Cantonmade 2025 Hotel Supplier Catalog: Technical Specs for Engineers & Integ...Cantonmade 2025 Hotel Supplier Catalog: Technical Specs for Engineers & Integ...
Cantonmade 2025 Hotel Supplier Catalog: Technical Specs for Engineers & Integ...
RayChan91
?
2025 DeveloperWeek - The Sound of Innovation: Why Voice Cloning Will Redefine...
2025 DeveloperWeek - The Sound of Innovation: Why Voice Cloning Will Redefine...2025 DeveloperWeek - The Sound of Innovation: Why Voice Cloning Will Redefine...
2025 DeveloperWeek - The Sound of Innovation: Why Voice Cloning Will Redefine...
David vonThenen
?

13 UIPopoverController and Modal View Controller

  • 1. UIPopoverController 和 Modal View Controller 范圣刚,princetoad@gmail.com, www.tfan.org
  • 2. ? 截?至??目前我们已经看到了有四种?方法可以?用来显 ?示?一个 view controller 的 view: ? 第?一种是可以设成 window 的 root view controller, view 就变成了 window 的 subview ? 第?二种是把 view controller 压?入?一个 UINavigationController 的堆栈 ? 第三种是我们可以把它添加到?一个 UITabBarController ? 以及模态化的把它呈现出来
  • 3. ? 在这个主题我们来看?一下 UIPopoverController, 以 及更多?用来呈现模态化 view controller 的选项 ? 有些选项只对 iPad ?生效,我们第?一步先把 Homepwner 改成 universal appication - 就是可以 本地化的在iPad,以及iPhone,iPod touch上运?行
  • 5. 改进界?面的两种?方式 ? 改成 Universal 之后,ItemsViewController 视图在 iPad 上看起来很好,但是如果选择?一?行的话,可 以看到 DetailViewController 的界?面可能需要?一些 加?工(?而且轻击获取图?片的按钮会抛出异常,后 ?面?马上会纠正这个问题)
  • 6. ? 改进 DetailViewController 界?面效果的?一种?方法是 设置 autosizing mask,让他的 iPad 上?自动调整来 适合 iPad ? 另外?一种是创建两个完全独?立的 XIB ?文件,?一个 针对 iPhone,?一个针对 iPad(加上~ipad 后缀) (要在iPad版本上重新创建视图和重新连接)。
  • 7. 背景颜?色 ? 现在我们先不关?心 DetailViewController 视图的显 ?示。我们先来处理?一下 iPhone 下的纯?白背景 ? 如果是 iPad 的话,我们把背景?色设成 groupTableViewBackgroundColor 颜?色;如果是 iPhone 的话,给它设定?一个和 ItemsViewController接近的颜?色
  • 8. 确定 device family ? ?首先我们要能够判断设备类型 ? 使?用 UIDevice 类的类?方法 currentDevice 可以得到当前 设备,然后通过检查它的 userInterfaceIdiom 属性判断 是何种设备。??目前有两个可能的值: ? UIUserInterfaceIdiomPad - iPad ? UIUserInterfaceIdiomPhone - iPhone 或 iPod touch
  • 9. userInterfaceIdiom: ? 在 DetailViewController.m 中修改 viewDidLoad: ? 我们在后?面会经常?用到这种判断 - (void)viewDidLoad { [super viewDidLoad]; // [[self view] setBackgroundColor:[UIColor groupTableViewBackgroundColor]]; UIColor *clr = nil; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { clr = [UIColor groupTableViewBackgroundColor]; } else { clr = [UIColor colorWithRed:0.875 green:0.88 blue:0.91 alpha:1]; } [[self view] setBackgroundColor:clr]; }
  • 10. ?自动翻转设置 ? 我们要让应?用在 iPad 上?支持所有?方向的翻转,在 iPhone 上只?支持 Portrait ? 在 ItemsViewController.m 和 DetailViewController.m 中都增加下?面的?方法 - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation { if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { return YES; } else { return (toInterfaceOrientation == UIInterfaceOrientationPortrait); } } 构建并运?行可以看到在 iPad 上视图可以进?行翻转
  • 12. ? 现在 Homepwner 已经可以在 iPad 上运?行了,我们 可以充分利?用 iPad 特有的?方法来呈现 view controller ? ?首先我们来看?一下 UIPopoverController ? 应?用程序经常需要呈现?一个 view controller 给?用户, 让?用户选择下?一步做什么。?比如 image picker。 ? 在 iPhone 和 iPod touch 上,这类 view controller 通 常是模态化呈现,占满整个屏幕。 ? 有更多屏幕空间的 iPad 有另外?一种选择: UIPopoverController
  • 13. ? popover controller 在应?用程序界?面的上?面浮动显 ?示另?一个 view controller 的 view,显?示在?一个边 框窗?口中。 ? 当我们?生成?一个 UIPopoverController 时,我们把 这另?一个 view controller 设成了 popover controller 的 contentViewController ? 我们要实现的是,?用户轻击 camera 按钮时,我们 在?一个 UIPopoverController ?里?面把 UIImagePickerController 呈现出来
  • 15. popover controller 声明 ? 在 DetailViewController 中增加?一个实例变量来持 有 popover controller;同时声明 DetailViewController 符合 UIPopoverControllerDelegate @interface DetailViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate, UIPopoverControllerDelegate> { __weak IBOutlet UITextField *nameField; __weak IBOutlet UITextField *serialNumberField; __weak IBOutlet UILabel *dateLabel; __weak IBOutlet UITextField *valueField; __weak IBOutlet UIImageView *imageView; UIPopoverController *imagePickerPopover; }
  • 16. 创建,设置和呈现 popover ? 如果是 iPad 的话,获取图?片时?用 popover controller;在 DetailViewController.m 中,在 takePicture: 末尾增加下列代码 [imagePicker setDelegate:self]; // 把 image picker 放到屏幕上 // [self presentViewController:imagePicker animated:YES completion:nil]; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { // 创建?一个新的将要显?示 imagePicker 的 popover control imagePickerPopover = [[UIPopoverController alloc] initWithContentViewController:imagePicker]; [imagePickerPopover setDelegate:self]; [imagePickerPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } else { [self presentViewController:imagePicker animated:YES completion:nil]; } }
  • 17. 释放 popover ? 构建并在 iPad 模拟器中运?行,导航到 DetailViewController 然后点击 camera 按钮,pop over 会出现并且显?示 image picker。选择?一个图 ?片,会出现在 DetailViewController 的 view 上。 ? 我们可以点击屏幕上的任何位置来释放这个 popover controller。当 popover 被以这种?方式释 放时,它发送?一个 popoverControllerDidDismissPopover: 消息给它 的 delegate
  • 18. popoverControllerDidDismissPopover: - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { NSLog(@"释放 popover"); imagePickerPopover = nil; } ? 在 DetailViewController.m 中实现这个?方法 ? 注意这?里我们把 imagePickerPopover 设成 nil 来销 毁它,我们每次点击 camera 按钮时都会?生成?一个 新的
  • 19. 选完图?片后释放 popover ? 在DetailViewController.m 中 imagePickerController:didFinishPickingMediaWithInfo : 末尾增加代码来释放 popover [imageView setImage:image]; // 把 image picker 从屏幕上移?走,必须调?用这个 dismiss ?方法 // [self dismissViewControllerAnimated:YES completion:nil]; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { [self dismissViewControllerAnimated:YES completion:nil]; } else { [imagePickerPopover dismissPopoverAnimated:YES]; imagePickerPopover = nil; } } ? 因为显式发送 dismissPopoverAnimated: 消息释放时,不会 发送 popoverControllerDidDismissPopover: 给它的 delegate,所有这?里要设置 imagePickerPopover 为 nil
  • 20. 销毁不可?见的 popover controller ? 还有?一个?小 bug 要处理,UIPopoverController 可 ?见的情况下,再次点击 camera 按钮,应?用会崩溃 ? 原因是点击 camera 按钮,在 takePicture: 中 imagePickerPopover 被设成了新的 UIPopoverController,这时屏幕上的 UIPopoverController 就被销毁。 ? 已经销毁的对象还是可?见的,就出现异常了。我 们要确保销毁的是不可?见的 UIPopoverController
  • 21. 已有,先释放 ? 在 DetailViewController.m 中,takePicker: 顶部增 加下?面的代码,增加对 imagePickerPopover 是否 可?见的判断 ? 这就实现了点击 camera,如果当前有显?示 popover,先释放 - (IBAction)takePicture:(id)sender { // 避免多次点击camera按钮引起异常 if ([imagePickerPopover isPopoverVisible]) { [imagePickerPopover dismissPopoverAnimated:YES]; imagePickerPopover = nil; return; } UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
  • 23. ? 在这个部分,我们更新 Homepwner,让它在?用户 新建?一个 BNRItem 时模态化的呈现 DetailViewController ? 当?用户选择?一个已经存在的 BNRItem 时,我们和 之前?一样把 DetailViewController 压?入 UINavigationController 的堆栈
  • 25. 双重?用法 ? 要实现 DetailViewController 的双重?用法(new 或 edit),我们要给它?一个名为 initForNewItem: 的新 的 designated initializer ? 在这个 initializer 中检查这个实例是被?用来?生成?一 个新的 BNRItem 还是?用来显?示?一个已经存在的, 然后对应的来设置?用户界?面 ? ?首先在 DetailViewController.h 中声明这个 initializer - (id)initForNewItem:(BOOL)isNew; @property (nonatomic, strong) BNRItem *item;
  • 26. Done 和 Cancel ? 如果 DetailViewController 被?用来?生成?一个新的 BNRItem,我们希望在它的 navigation item 上显?示 ?一个 Done 按钮和?一个 Cancel 按钮 ? 在 DetailViewController.m 中实现这个?方法
  • 27. 实现 initForNewItem: - (id)initForNewItem:(BOOL)isNew { self = [super initWithNibName:@"DetailViewController" bundle:nil]; if (self) { if (isNew) { UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(save:)]; [[self navigationItem] setRightBarButtonItem:doneItem]; UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)]; [[self navigationItem] setLeftBarButtonItem:cancelItem]; } } return self; } ? 如果是新建的话,设置左右按钮
  • 28. 重写超类 designated initializer ? 之前我们更改 designated initializer 的话,会重写 超类的 initializer 来调?用新的。这?里我们通过抛出 ?一个异常来使对超类 designated initializer 的调?用 ?非法 ? 在 DetailViewController.m 中,重写 UIViewController 的 designated initializer - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { @throw [NSException exceptionWithName:@"Wrong initializer" reason:@"Use initForNewItem" userInfo:nil]; return nil; } ? NSException。异常时,name 和 reason 会在控制台 显?示
  • 29. 确认异常 ? ??目前在 ItemsViewController 的 tableView:didSelectRowAtIndexPath: ?方法中调?用 initWithNibName:bundle: ?方法(对 init ?方法的调?用 最终会调?用 initWithNibName:bundle :?方法) ? 构建并运?行,在表格中选择?一?行的结果是 “Wrong initializer”异常被抛出,程序挂起,可以在控制台 中看到?一个异常(name 和 reason)
  • 30. 使?用新的 initializer ? 为了消除这个异常,我们在 ItemsViewController.m 中更新 tableView:didSelectRowAtIndexPath: 以使?用新的 initializer - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath { // DetailViewController *detailViewController = [[DetailViewController alloc] init]; DetailViewController *detailViewController = [[DetailViewController alloc] initForNewItem:NO]; NSArray *items = [[BNRItemStore defaultStore] allItems];
  • 31. ? 现在我们已经能让新的 initializer ?工作了,下?面更 改?一下?用户增加?一个新的 item 时我们的操作 ? 在 ItemsViewController.m 中,编辑 addNewItem: 来在 UINavigationController 中创建?一个 DetailViewController 的实例,并且把这个 navigation controller 模态化的呈现
  • 32. 修改 ItemsViewController.m 中addNewItem - (IBAction)addNewItem:(id)sender { 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]; DetailViewController *detailViewController = [[DetailViewController alloc] initForNewItem:YES]; [detailViewController setItem:newItem]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailViewController]; [self presentViewController:navController animated:YES completion:nil]; } ? 构建并运?行,DetailViewController 从底部滑出
  • 34. ? 要释放(dismiss)?一个模态化呈现的 view controller,必须给呈现它的 view controller 发送 dismissViewControllerAnimated:completion: 消息 ? 我们在之前的 DetailViewController 中,当 UIImagePickerController 完成?工作并告诉 DetailViewController 以后, DetailViewController 就 把它释放了
  • 35. ? 现在的情况稍有不同。当?一个新的 item 被?生成 时,ItemsViewController 模态化的呈现这个 DetailViewController。 ? 这个 DetailViewController 在它的 navigationItem 上有两个按钮:Cancel 和 Done,按钮按下时 DetailViewController 将被释放
  • 36. ? 问题是,这些按钮的 action message 是发给 DetailViewController 的,DetailViewController 需 要?一种?方法来告诉呈现它的并且负责释放它的 ItemViewController,它已经完?工可以被释放了
  • 37. presentingViewController 属性 ? 幸运的是,每个 UIViewController 都有?一个 presentingViewController 属性,这个属性指向呈 现它的 view controller ? DetailViewController 将会获取?一个指向它的 presentingViewController 的指针,并把 dismissViewControllerAnimated:completion: 消息 发送给它
  • 38. 实现 save action ?方法 ? 在 DetailViewController.m 中实现为 Done 按钮实 现它的 save action ?方法 - (void)save:(id)sender { [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; }
  • 39. Cancel -> 删除新建的 item ? Cancel 按钮需要多做?一点?工作。 ? ?用户点击 ItemViewController 的按钮添加?一个新的 item 到列表,就创建了?一个新的 BNRItem 实例, 添加到 store,然后 DetailViewController 才滑动出 来供我们编辑 ? 如果?用户取消了这个 item 的?生成,那么这个 BNRItem 就需要从 store 中删除 ? 所以我们需要在 Cancel 结束后多做?一些?工作
  • 40. Cancel 的实现 ? ?首先要在 DetailViewController.m 中导?入 BNRItemStore 的头?文件 #import "DetailViewController.h" #import "BNRItem.h" #import "BNRImageStore.h" #import "BNRItemStore.h" - (void)cancel:(id)sender { [[BNRItemStore defaultStore] removeItem:item]; [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; } ? 构建并运?行,新建然后cancel,不会有内容添加到 table view;新建,然后 Done, DetailViewController 将滑出屏幕
  • 41. action message 的验证 ? 注意 cancel: 和 save: ?方法没有在任何地?方做声 明,这并没有什么问题 ? 声明?一个?方法是为了让编译器知道这个?方法的存 在,在设置 UIBarButtonItem 和 UIControl 的 action 时,编译器不会验证 action message,因为此时 并没有调?用。消息是在运?行时实际被发送时才被 验证 ? 如果这个?方法被定义了,将会运?行良好;否则就 会得到?一个 unrecognized selector exception
  • 44. ? 在 iPhone 和 iPod touch上,?一个模态化 view controller 会展满整个屏幕,是默认的?而且也只能 这样 ? 在 iPad 上,我们有两个附加选项: ? form sheet style ? 和 page sheet style
  • 45. ? 我们可以通过设置modal view controller 的 modalPresentationStyle 属性为?一个预设的常量: ? UIModalPresentationFormSheet ? 或 UIModalPresentationPageSheet 来更改这个呈现
  • 46. form sheet style ? form sheet style 在 iPad 屏幕中间的?一个矩形 内显?示模态化 view controller,并且把呈 现它的 view controller 的 view 变暗
  • 47. page sheet style ? 在 portrait 模式下和默 认全屏相同 ? 在 landscape 模式下, 保持它的宽度和 portrait 模式下相同, 并且把贴在它下?面的 呈现它的 view controller 的左右边变 暗
  • 48. 改变 presentation style ? 在 ItemsViewController.m 中修改 addNewItem: ?方 法来变更被呈现的 UINavigationController 的 presentation style UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailViewController]; [navController setModalPresentationStyle:UIModalPresentationFormSheet]; [self presentViewController:navController animated:YES completion:nil];
  • 49. 新的 item 不?见了? ? 再次构建并运?行,点击按钮增加?一个新的item, modal view controller 滑?入屏幕。然后点击 Done 按钮,table view 重新出来了,但是新的 BNRItem 并没有显?示出来!??
  • 50. ? 变更 presentation style 前,modal view controller 是全屏显?示的,这引起了 ItemsViewController 的 view disappear。 ? 当 modal view controller 被 dismiss 后, ItemsViewController 被发送 viewWillAppear: 消 息,然后我们是在 viewWillAppear: 中重新加载所 有新加的数据
  • 51. table view 重新加载数据 ? 当使?用新的 presentation style 时, ItemsViewController 的 view 在它呈现 view controller 的时候并没有消失(只是颜?色变暗 了)。 ? 因?而在 modal view controller 被 dismissed 的时候 没有被发送重新出现的消息,因此也没有机会重 新加载数据。
  • 52. ? ItemsViewController 重新加载它的 table view 的代 码很简单: ? 类似:[[self tableview] reloadData] ? 但是我们必须找到?一个机会来重新加载这些数据
  • 53. ? 我们需要做的是打包上?面的代码,然后恰好在 modal view controller 被 dismissed 的时候让它执 ?行 ? 我们可以使?用 dismissViewControllerAnimated:completion: 中?一 个内置的机制来完成这个
  • 55. ? 我们可以把重新加载 table view 的代码放?入?一个 block,然后传给 dismissViewControllerAnimated:completion: , 然 后代码就正好在 modal view controller 被 dismissed 时候执?行 ? 在 DetailViewController.h 中,增加?一个指向?一个 block 的属性,在 .m 中 synthesize @property (nonatomic, copy) void (^dismissBlock)(void); @implementation DetailViewController @synthesize item; @synthesize dismissBlock;
  • 56. 传递 block ? 我们不能在 DetailViewController ?自?身中创建这个 block,?而只能在 ItemsViewController 中创建,只 有 ItemsViewController 才知道它的 tableView ? 在 ItemsViewController.m 中的 addNewItem: ?方法 中,?生成?一个重新加载 ItemsViewController 的表 格的 block,并把它传给 DetailViewController - (IBAction)addNewItem:(id)sender { BNRItem *newItem = [[BNRItemStore defaultStore] createItem]; DetailViewController *detailViewController = [[DetailViewController alloc] initForNewItem:YES]; [detailViewController setItem:newItem]; [detailViewController setDismissBlock:^{ [[self tableView] reloadData]; }]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
  • 57. ? 这时当?用户再点击按钮添加?一个新的 item 时,?一 个重新加载 ItemsViewController 的表格的 block 会 被创建,并被设成 DetailViewController 的 dismissBlock。 ? DetailViewController 将会持有这个block 直到 DetailViewController 需要被释放 ? 在被释放时,DetailViewController 将把这个 block 传给 dismissViewControllerAnimated:completion:
  • 58. ? 在 DetailViewController.m 中修改 save: 和 cancel: 的实现,使?用 dismissBlock 作为?一个参数发送 dismissViewControllerAnimated:completion: 消息 ? 再构建并运?行,新增加的 BNRItem 就会出现在表 格中了 - (void)cancel:(id)sender { [[BNRItemStore defaultStore] removeItem:item]; // [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; [[self presentingViewController] dismissViewControllerAnimated:YES completion:dismissBlock]; } - (void)save:(id)sender { // [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; [[self presentingViewController] dismissViewControllerAnimated:YES completion:dismissBlock]; }
  • 60. ? 最后我们看?一下 modal view controller 的 transitions。 ? 除了可以更改 modal view controller 的 presentation style 之外,我们也可以更改把它放 置到屏幕上时的动画效果。
  • 61. ? 使?用 modalTransitonStyle 属性,可以设置?一个预 定义的常量。 ? 默认是将从屏幕底部滑出到屏幕 ? 在 ItemsViewController.m 中更新 addItmName: ?方 法使?用?一个不同的 transition(横向翻动) [navController setModalPresentationStyle:UIModalPresentationFormSheet]; [navController setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal]; [self presentViewController:navController animated:YES completion:nil];