彈出框是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]; }