首页 » iOS编程(第4版) » iOS编程(第4版)全文在线阅读

《iOS编程(第4版)》17.3 UIPopoverController

关灯直达底部

为了有效利用充足的屏幕空间,Apple针对iPad应用提供了UIPopoverController。在iPhone应用中,如果要让用户做一个选择(例如在照片库中选择一张照片),或者针对屏幕上的某些元素显示相关摘要信息(例如向用户解释某一项数据的具体含义),则可能会使用模态视图控制器或警告视图;而在iPad应用中,这些场景更适合使用UIPopoverController。对于Homepwner应用,当用户在详细界面点击相机按钮时,就可以使用UIPopover- Controller来显示UIImagePickerController。

UIPopoverController能够在一个带边框的窗口中显示一个指定的视图控制器的视图。此外,这个窗口会“悬浮”在其他界面的前面。创建UIPopoverController对象时需要设置contentViewController属性,指向需要显示的视图控制器。

本节要完成的任务为:当用户按下相机按钮时,通过UIPopoverController对象来显示UIImagePickerController对象(见图17-2)。

图17-2 UIPopoverController

在BNRDetailViewController.m的类扩展中,将BNRDetailViewController声明为遵守UIPopoverControllerDelegate协议,代码如下:

@interface BNRDetailViewController ()

<UINavigationControllerDelegate, UIImagePickerControllerDelegate,

UITextFieldDelegate,UIPopoverControllerDelegate>

再添加一个属性,用于保存UIPopoverController对象,代码如下:

@interface BNRDetailViewController ()

<UINavigationControllerDelegate, UIImagePickerControllerDelegate,

UITextFieldDelegate, UIPopoverControllerDelegate>

@property (strong, nonatomic) UIPopoverController *imagePickerPopover;

@property (weak, nonatomic) IBOutlet UITextField *nameField;

然后在takePicture:方法的末端添加以下代码:

imagePicker.delegate = self;

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

// 显示UIImagePickerController对象

// 创建UIPopoverController对象前先检查当前设备是否是iPad

if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {

// 创建UIPopoverController对象,用于显示UIImagePickerController对象

self.imagePickerPopover = [[UIPopoverController alloc]

initWithContentViewController:imagePicker];

self.imagePickerPopover.delegate = self;

// 显示UIPopoverController对象,

// sender指向的是代表相机按钮的UIBarButtonItem对象

[self.imagePickerPopover

presentPopoverFromBarButtonItem:sender

permittedArrowDirections:UIPopoverArrowDirectionAny

animated:YES];

} else {

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

}

}

这段代码在创建UIPopoverController对象前,要先检查设备的类型,这点很重要。只有当iOS应用是在iPad系设备上运行的时候,才能创建UIPopoverController对象,否则应用会抛出异常。

针对iPad模拟器或iPad构建并运行应用。选中UITableView对象中的某一行,显示BNRDetailViewController对象的视图并按下相机按钮。Homepwner会显示一个UIPopoverController对象并显示UIImagePickerController对象的视图。从UIImagePicke- Controller对象所显示的照片中挑选一张,Homepwner会关闭UIPopoverController对象,并在BNRDetailViewController对象的视图中显示选中的照片。

触摸屏幕的其他区域可以关闭UIPopoverController对象。如果某个UIPopoverController对象是通过这种方式关闭的,那么该对象会向自己的委托对象发送popoverControllerDidDismissPopover:消息。在BNRDetailViewController.m中实现popoverControllerDidDismissPopover:,代码如下:

- (void)popoverControllerDidDismissPopover:

(UIPopoverController *)popoverController

{

NSLog(@“User dismissed popover”);

self.imagePickerPopover = nil;

}

为了放弃针对UIPopoverController对象的拥有权,这段代码在popoverController- DidDismissPopover:中将imagePickerPopover赋为了nil。每当用户按下拍照按钮时,Homepwner都会创建一个新的UIPopoverController对象。

当用户在UIImagePickerController对象中选择一张图片后,也要关闭UIPopoverController对象。在BNRDetailViewController.m中的imagePickerController: didFinishPickingMediaWithInfo:末尾关闭UIPopoverController对象,代码如下:

self.imageView.image = image;

[self dismissViewControllerAnimated:YES completion:nil];

// 判断UIPopoverController对象是否存在

if (self.imagePickerPopover) {

// 关闭UIPopoverController对象

[self.imagePickerPopover dismissPopoverAnimated:YES];

self.imagePickerPopover = nil;

} else {

// 关闭以模态形式显示的UIImagePickerController对象

[self dismissViewControllerAnimated:YES completion:nil];

}

}

要“主动地”关闭UIPopoverController对象,可以向需要关闭的UIPopoverController对象发送dismissPopoverAnimated:消息。如果UIPopoverController对象是通过这种方式关闭的,就不会向其委托对象发送popoverControllerDidDismissPopover:消息。为了应对这种情况,还需要在关闭UIPopoverController对象之后将其设置为nil。

以上代码有一个小问题需要修正。在用户按下拍照按钮打开UIPopoverController对象后,如果再次按下拍照按钮,Homepwner就会崩溃。这是因为当用户再次按下按钮时,触发的takePicture:会创建新的UIPopoverController对象,并将新创建的对象赋给imagePickerPopover,从而导致当前可见的UIPopoverController对象失去其最后的一个拥有方并被释放。要解决这个问题,只要在创建并显示新的UIPopoverController对象前,检查imagePickerPopover是否指向有效的UIPopoverController对象。如果是有效的,并且该对象的视图是可见的,就关闭这个对象,而不是重新创建新的,代码如下:

- (IBAction)takePicture:(id)sender

{

if ([self.imagePickerPopover isPopoverVisible]) {

// 如果imagePickerPopover指向的是有效的UIPopoverController对象,

// 并且该对象的视图是可见的,就关闭这个对象,并将其设置为nil

[imagePickerPopover dismissPopoverAnimated:YES];

imagePickerPopover = nil;

return;

}

UIImagePickerController *imagePicker =

[[UIImagePickerController alloc] init];

构建并运行应用。按下拍照按钮并显示UIPopoverController对象,然后再次按下拍照按钮,之前显示的UIPopoverController对象就会消失。