先看他的繼承關係,UIPopoverController是直接繼承自NSObject,它和UIViewController沒有關係.那它是怎麼實現彈出在全部View之上的,我猜想是利用了keywindow,把這個View加在keywindow裏面,我作了個試驗,通常咱們會在AppDelegate的didFinishLaunchingWithOptions中來初始化咱們的window,把應用的第一個viewcontroller加到window中去,並在最後調用window的makekeyandvisible方法。因而我嘗試在window實例調用makekeyandvisible方法的以前彈出一個UIPopoverController,因而獲得了下面的錯誤:數組
***Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:'-[UIPopoverController presentPopoverFromRect:inView:permittedArrowDirections:animated:]: Popovers cannot be presented from a view which does not have a window.'app
因此我猜UIPopoverController就是把你提供的Viewcontroller包起來加一個arrow框和背景,加到keywindow中去。另外若是你在顯示地時候傳入的BarButtonItem爲nil,也會報這個錯誤,但實際上和window無關。ide
使用注意:優化
1、.UIPopoverController該控制器的內容必須由一個控制器提供;提供方式有3個:ui
一、atom
- (id)initWithContentViewController:(UIViewController *)viewControllerspa
//很簡單的初始化方法,把你要展現的Viewcontroller傳給它。代理
二、繼承
-(void)setContentViewController:(UIViewController*)viewController animated:(BOOL)animatedip
//能夠在UIPopoverController還在顯示的時候動態地更換ContentViewController。
三、
@property (nonatomic, retain) UIViewController *contentViewController
2、設置內容大小:
/* This property allows direction manipulation of the content size of the popover. Changing the property directly is equivalent to animated=YES. The content size is limited to a minimum width of 320 and a maximum width of 600. */ @property (nonatomic) CGSize popoverContentSize; - (void)setPopoverContentSize:(CGSize)size animated:(BOOL)animated;
/* contentSizeForViewInPopover allows you to set the size of the content from within the view controller. This property is read/write, and you should generally not override it. */ @property (nonatomic,readwrite) CGSize contentSizeForViewInPopover NS_AVAILABLE_IOS(3_2);
三.設置箭頭方向:
@property (nonatomic, readonly) UIPopoverArrowDirection popoverArrowDirection
typedef NS_OPTIONS(NSUInteger, UIPopoverArrowDirection) {
UIPopoverArrowDirectionUp = 1UL << 0,
UIPopoverArrowDirectionDown = 1UL << 1,
UIPopoverArrowDirectionLeft = 1UL << 2,
UIPopoverArrowDirectionRight = 1UL << 3,
UIPopoverArrowDirectionAny = UIPopoverArrowDirectionUp | UIPopoverArrowDirectionDown | UIPopoverArrowDirectionLeft | UIPopoverArrowDirectionRight,
UIPopoverArrowDirectionUnknown = NSUIntegerMax
};
4、
一、若是從一個導航按鈕處呈現,使用:
presentPopoverFromBarButtonItem:permittedArrowDirections:animated:;
這種方式彈出的popVC接近於模態, 但不徹底是模態, 由於NavigationBar上的全部按鈕都高於這個popVC層, 也就是說NavigationBar上的按鈕能夠繼續響應用戶的操做,而無論當前是否有從BarButtonItem彈出的popVC。
這樣就有可能引起一些問題, 如, 咱們再次點擊這個BarButtonItem時, 則又會執行一次彈出操做, 實際上界面上將會有兩個popOver, 更明顯的問題是, 若是咱們點navigationBar上的返回按鈕,把當前這個界面pop出去, 則會由於當前還有展現的popVC而使當前界面崩潰。
通常實現:
- (IBAction)languageButtonTapped {
if (self.languagePopoverController == nil) {
BIDLanguageListController *languageListController =
[[BIDLanguageListController alloc] init];
languageListController.detailViewController = self;
UIPopoverController *poc = [[UIPopoverController alloc]
initWithContentViewController:languageListController];
[poc presentPopoverFromBarButtonItem:languageButton
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
self.languagePopoverController = poc;
} else {
if (languagePopoverController != nil) {
[languagePopoverController dismissPopoverAnimated:YES];
self.languagePopoverController = nil;
}
}
}
二、若是要從一個視圖出呈現,使用:
presentPopoverFromRect:inView:permittedArrowDirections:animated:
這個方法須要傳入一個CGRect和一個View,而只有這個CGRect的值是相對於這個View的,這個Arrow才能指到正確的位置。我猜想它是在視圖樹中向上搜索把它轉化爲在keywindow中的值再顯示。舉個例子,若是你要箭頭指向被點擊的這個button,
那麼一種方法是:
[xxx presentPopoverFromRect:button.bounds inView:button permittedArrowDirections:xx animated:YES];
一種方法是轉換爲他的父視圖中的CGRect:
[xxx presentPopoverFromRect:button.frame inView:button.superview permittedArrowDirections:xx animated:YES];
// inView的中心點是用來畫箭頭的,若是中心點若是出了屏幕,系統會優化到窗口邊緣
//inView是個CGRectMake(x0, y0, x1, y1),若是你想絕對定位的話,能夠把x1,y1設置爲0,x0,y0就是箭頭的位置。
這種方式彈出的popVC就是絕對的模態了, 不把這個popVC消隱, 其它任何地方, 包括NavigtionBar都獲得不交互。
注意:若是設備旋轉之後,位置定位錯誤須要在父視圖控制器的下面方法裏面從新定位:
didRotateFromInterfaceOrientation:(在這個方法體裏面從新設置rect)
而後再次調用:
- (void)presentPopoverFromRect:(CGRect)rect inView:(UIView *)view permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated
5、UIPopoverController的外觀
經過popoverBackgroundViewClass屬性和popoverLayoutMargins,你就能夠本身定製Popover的外觀了,popoverLayoutMargins是指你的popover相對於整個window上下左右的margin,當你設置的值大於它目前的值的時候,它纔會調整(也就是讓它本身更小的margin纔會被實現).另外經過subclass UIPopoverBackgroundView,並把該class指定給popoverBackgroundViewClass屬性,你就能夠隨意改變他的外觀了。
6、UIPopoverController防止點擊區域外消失
UIPopoverController的默認行爲是,當你點擊UIPopoverController之外的區域時,它會消失,改變這種行爲就要利用屬性passthroughViews.它的意思是,UIPopoverController點擊在它的區域外,若是點到了passthroughViews中的View的時候,是不會消失的。我寫了demo試了一下,passthroughView中被點擊的View以及其SubView都不會讓UIPopoverController消失。
通常點擊區域外消失實現代理來處理後續動做:
- (void)popoverControllerDidDismissPopover:(UIPopoverController*)popoverController{
if (popover) {
[popover dismissPopoverAnimated:YES];
[popover release];
popover=nil;
if (popoverContent != nil) {
[popoverContent release];
popoverContent = nil;
}
}
}
7、UIPopoverController的內存管理
根據目前我使用的狀況來看,我經常使用的使用方式是在屬性中聲明一個 retain 的 UIPopoverController,而後在建立的時候指向建立的臨時變量,而後釋放臨時變量.我尚未發現更方便的內存管理的方法,UIPopoverController和UIActionSheet有點不同,你必須本身來retain這個UIPopoverController,若是在UIPopoverController尚未dismiss的時候你就release掉了,就會出錯。固然,你能夠把UIPopoverController的delegate設爲self,而後在popoverControllerDidDismissPopover中釋放他,但我感受這樣還不如採用屬性更方便,由於你無法代碼控制popover的消失了,總得維護一個引用,固然,象我這種方法得話,你不少時候是在延遲釋放這個popOver了。
通常來講,你能夠常用這樣得代碼:
[self.pop dismissPopoverAnimate:xx];
self.pop=nil;
由於給nil發送一個dismissxx是沒有問題的,而後再釋放,這樣能夠保證內存不混出錯。固然,若是實在不放心,能夠老是在前面加一個if(!=nil)的判斷。
7、
多個UIPopoverController的切換問題
狀況描述:多個button控制對應的UIPopoverController,當一個UIpopverController_A打開的時候,點擊button_B去打開另一個UIPopverContrller_B,每次都須要點擊兩下才能打開,(個人理解)第一次只是關閉UIpopverController_A,第二次纔是打開UIPopverContrller_B。
解決方法:
UIPopoverController * poper...
UIButton * BtnA...
NSArray *array=[NSArray arrayWithObjects:BtnA,BtnB,BtnC,BtnD,BtnE,BtnF,BtnG,BtnH];
poper.passthroughViews=array;
設置passthroughViews爲這個數組就能夠了