弹出框是iPad的常用UI元素,即在现有视图上面显示内容,并通过一个小箭头指向一个屏幕对象(如按钮),以提供上下文。
和模态场景一样,弹出框的内容也由一个视图和一个试图控制器决定,不同之处在于,弹出框还需要另一个控制器对象--弹出框控制器(UIPopoverController)。该控制器指定弹出框的大小及其箭头指向何方。用户使用完弹出框后,只要触摸弹出框外面就可以自动关闭它。
如果是以拖拽的方式创建弹出切换,应该选择切换类型为"Popover",当这样选择后,发现可以调整视图的大小了;同时Interface Builder编辑器将该场景顶部的状态栏删除了,视图显示为一个平淡的矩形。这是因为弹出框作为一个小窗口显示在另一个视图上面,因此状态栏没有意义。
对于弹出框,Apple允许的最大宽度为600像素,但建议宽度不超过320像素;而允许的最大高度与iPad屏幕相同。
在编辑器中,可以通过"Directions"来设置箭头所指方向,例如设置为"left",则箭头指向左边,弹出框显示在元素右边。"Anchor"选项用来设置弹出框锚住的UI元素。"Passthrough"用来设置点击哪些元素后,弹出框并不消失。
手工显示弹出框仍然是调用performSegueWithIdentifier:sender。但是要注意的是,箭头并不指向sender参数提供的对象。所以只能以编程方式启动弹出切换,但必须在Interface Builder中将其关联到一个界面元素。
对于弹出框,是没有模态视图的presentingViewController和presentedViewController属性的,所以要在父视图中获取弹出框视图,必须通过prepareForSegue:sender方法。调用该方法后,先获取弹出框的专有控制器UIPopoverController实例,例如:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([segue.identifier isEqualToString:@"toEditPopover"]) { //将通用切换UIStoryboardSegue转换成弹出切换UIStoryboardPopoverSegue UIStoryboardPopoverSegue *popoverSegue = (UIStoryboardPopoverSegue *)segue;
//弹出切换有一个属性"popoverController"来获取弹出框控制器UIPopoverController UIPopoverController *popoverController = popoverSegue.popoverController;
//设置该控制器的delegate为自身类,用来处理弹出框关闭事件popoverControllerDidDismissPopover popoverController.delegate = self;
//通过属性"contentViewController"可以获得弹出框实例 EditController *editVC = (EditController *)popoverController.contentViewController; } }
弹出框关闭时,父视图控制器是无法直接获悉的。要捕获弹出框关闭时的事件并获取相关内容,需要在父视图里遵守协议UIPopoverControllerDelegate。该协议提供了一个方法popoverControllerDidDismissPopover,可通过实现它来响应弹出框关闭。在这个方法中,还可获取弹出框的内容视图器,并访问其属性。(另一个方法是通过UIViewController的方法viewWillDisppear,这个方法在视图控制器的内容从屏幕上删除(就弹出框而言,是弹出框关闭)时被调用。)
popoverControllerDidDismissPopover方法接受一个参数:弹出框的UIPopoverController。通过该对象的"contentViewController"属性可以获取弹出框的视图控制器。这个代理类方法用来处理多个弹出框关闭事件,要分辨contentViewController指向的对象属于哪个类,可以调用弹出框的isMemberOfClass来判断,例如:
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { if([popoverController.contentViewController isMemberOfClass:[EditController class]]) { EditController *editVC = (EditController *)popoverController.contentViewController; //do something... } }
要以编程方式创建并显示弹出框,必须先创建并配置UIPopoverController,然后调用UIPopoverController的initWithContentViewController来关联弹出框实例,最后调用UIPopoverController的presentPopoverFromRect : inView : permittedArrowDirections : animated方法显示弹出框。
第一步要先取得弹出框实例:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; EditController *editVC = [storyboard instantiateViewControllerWithIdentifier:@"myEdit"];
然后开始创建并配置UIPopoverController:
在iOS 5 和 Xcode 4.2(启用了ACR)中,有时会出现这样的问题:分配并初始化对象后,在我们需要使用前它们就被ACR释放了。这种情形不会经常发生,但一旦发生,应用程序就将崩溃,虽然从技术上说代码是正确的。为避免这种问题,可声明一个实例变量/属性,并让它指向要保留的对象。这种引用将避免ARC将该对象释放,从而确保一切按预期进行。
而UIPopoverController就是这种问题的受害者。如果您在同一个方法中声明、分配、配置和显示UIPopoverController,应用程序将崩溃。为了避免这种问题,将把它声明为一个属性:
@property (strong, nonatomic) UIPopoverController *editPopoverController;
@synthesize editPopoverController;
并在ViewController.m的方法viewDidUnload中执行清理工作,将该属性设置为nil:
- (void)viewDidUnload { [self setEditPopoverController:nil]; [super viewDidUnload]; }
然后关联弹出框实例:
self.editPopoverController = [[UIPopoverController alloc] initWithContentViewController:editVC];
接下来,使用UIPopoverController的属性popoverContentSize设置弹出框的宽度和高度。这个属性实际上是一个CGSize结构,该结构包含宽度和高度。为创建合适的CGSize结构,可使用函数CGSizeMake(),例如下面的代码将弹出框设置为宽300,高400:
self.editPopoverController.popoverContentSize = CGSizeMake(300, 400);
在显示弹出框之前,需要完成的最后一步是,设置弹出框控制器的委托,让弹出框控制器自动调用协议UIPopoverControllerDelegate定义的方法popoverControllerDidDismissPopover:
self.editPopoverController.delegate = self;
显示弹出框需要调用UIPopoverController的方法presentPopoverFromRect : inView : permittedArrowDirections : animated,各参数说明如下:
第一个参数是设定弹出框指向的对象范围,格式为CGRect,一般设置为((UIView *)sender).frame。因为添加到视图中的任何对象都是UIView的子类,而UIView类有一个frame属性来标示对象的范围。
第二个参数inView指向显示弹出框的视图。由于我们假定从ViewController类中显示该弹出框,因此将其设置为self.view。
第三个参数permittedArrowDirections用来设置箭头可指向的方向。有如下常量:
UIPopoverArrowDirectionAny -- 箭头可指向任何方向,这给iOS在确定如何显示弹出框时提供了最大的灵活性。
UIPopoverArrowDirectionUp -- 箭头只能指向上方,这意味着弹出框必须位于对象下方。
UIPopoverArrowDirectionDown -- 箭头只能指向下方,这意味着弹出框必须位于对象上方。
UIPopoverArrowDirectionLeft -- 箭头只能指向左方,这意味着弹出框必须位于对象右边。
UIPopoverArrowDirectionRight -- 箭头只能指向右方,这意味着弹出框必须位于对象左边。
Apple建议尽可能使用常量UIPopoverArrowDirectionAny。显示弹出框时,可结合使用多个箭头方向,方法是使用管道线( | )分割这些常量。
第四个参数animated设置是否以动画的方式显示,通常为YES。
代码演示如下:
- (IBAction)testSomething:(id)sender { UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; EditController *editVC = [storyboard instantiateViewControllerWithIdentifier:@"myEdit"]; self.editPopoverController = [[UIPopoverController alloc] initWithContentViewController:editVC]; self.editPopoverController.popoverContentSize = CGSizeMake(300, 400); self.editPopoverController.delegate = self; [self.editPopoverController presentPopoverFromRect:((UIView *)sender).frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; }