为了有效利用充足的屏幕空间,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对象就会消失。