何為UIPopoverPresentationController?
An object that manages the display of content in a popover.--一個用來管理在一個彈出窗口中展示內容的對象
先來看一下效果
iOS9.0廢棄的UIPopoverController
可以通過這篇文章了解一下以前的UIPopoverController使用方法UIPopoverController的使用
轉到iOS8.0之后的UIPopoverPresentationController
官方文檔對UIPopoverPresentationController的介紹原文為:
From the time a popover is presented until the time it is dismissed, UIKit uses an instance of this class to manage the presentation behavior. You use instances of this class as-is to configure aspects of the popover appearance and behavior for view controllers whose presentation style is set to popover
.
意思是說:從一個popover(也就是彈出的窗口)被presented(呈現(xiàn))到被dismissed(退出)的期間丙躏,UIkit使用UIPopoverPresentationController的實例來管理呈現(xiàn)的行為烙丛。我們可以使用這個實例來為那些呈現(xiàn)的樣式為popover的控制器設置popover的各個方面的外觀和行為
另外官方文檔還提到:
In nearly all cases, you use this class as-is and do not create instances of it directly. UIKit creates an instance of this class automatically when you present a view controller using the popover
style. You can retrieve that instance from the presented view controller’s popoverPresentationController
property and use it to configure the popover behavior.
意思是說:在幾乎所有的情況下,我們都應該使用這個類并且不要直接創(chuàng)建它的實例第焰。因為當我們present(呈現(xiàn)/彈出)一個控制器使用popover樣式的時候,UIKit會自動為我們創(chuàng)建一個這個類的實例。我們可以通過控制器的popoverpresentationcontroller屬性來重新獲取UIPopoverPresentationController的實例诽嘉,并且使用它來設置我們的popover的行為
如果當我們presenting一個控制器的時候,我們不想立即配置popover的話,我們可以使用它的代理去設置它虫腋。在present控制器的過程當中骄酗,UIPopoverPresentationController實例會調用遵守UIPopoverPresentationControllerDelegate的對象的很多方法,來詢問一些信息和告訴它關于它應該呈現(xiàn)的狀態(tài)
官方提供了如下的使用Swift代碼的例子(我從Swfit切換到Objective-C悦冀,但還是顯示的Swfit的例子??趋翻,感覺蘋果要默默干掉OC)
@IBAction func displayOptionsForSelectedItem () {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let optionsVC = storyboard.instantiateViewController(withIdentifier: "itemOptionsViewController")
optionsVC.modalPresentationStyle = .popover
optionsVC.popoverPresentationController?.barButtonItem = optionsControl
self.present(optionsVC, animated: true) {}
}
官方提供的示例代碼就是簡單的把設置了一下將要彈出的控制器的modalPresentationStyle和popoverPresentationController用來顯示在什么位置。但是使用上述代碼運行的話盒蟆,我們會發(fā)現(xiàn)它竟然把要彈出的控制器全屏展示了(這可能不是我們想要的效果)
定制我們想要的popopver效果(介紹)
Customizing the Popover Behavior(定制popover的行為)
-
delegate : UIPopoverPresentationControllerDelegate?
使用控制器的popoverPresentationController的delegate屬性定制我們想要的效果
Configuring the Popover Appearance(設置popover的外觀)
-
popoverLayoutMargins : UIEdgeInsets
定義popover允許展示的區(qū)域的各部分間隙踏烙。
客戶端可能想要改變展示的popover的可用范圍。默認的是返回距離展示的popover的各邊的10個點历等,并且彈出的popover總是占據(jù)狀態(tài)欄(我試了一下點擊狀態(tài)欄讨惩,popover不會消失),矩形的inset總是以當前設備的朝向來表示寒屯,(0荐捻,0)一直在設備的左上角。這可能需要在設備的旋轉上進行改變寡夹。我設置了一下這個屬性处面,即使在選擇的情況下,好像沒看到什么改變菩掏。 -
backgroundColor: UIColor?
popover的背景視圖顏色 -
passthroughViews : [UIView]?
這個屬性里可以放置那些當popover顯示的時候的可和用戶交互的視圖 -
popoverBackgroundViewClass : UIPopoverBackgroundViewMethods.Type?
這個屬性可以是UIPopoverBackgroundView的子類魂角,也可以自定義一個UIView但是必須遵守UIPopoverBackgroundViewMethods協(xié)議并實現(xiàn)這個協(xié)議的響應方法。這個協(xié)議可以設置箭頭的位置患蹂、高度以及它的contentView的inset -
canOverlapSourceViewRect : Bool
表示popover是否可以和它的源視圖矩形重疊或颊,默認是false。如果為true的話传于,表示popover的內容比可用空間更大時被允許重疊源視圖以容納內容
Specifying the Popover’s Anchor Point(指定popover的錨點)
錨點也就是popover的箭頭所指向的位置
-
barButtonItem: UIBarButtonItem?
指定popover指向UIBarButtonItem -
sourceView : UIView?
指定popover指向的View -
sourceRect : CGRect
指定popover指向的矩形
Configuring the Popover Arrows(設置popover的箭頭)
-
permittedArrowDirections: UIPopoverArrowDirection
表示允許的方向 -
arrowDirection: UIPopoverArrowDirection
獲取箭頭的指向囱挑。在彈出之前,這個值是UIPopoverArrowDirectionUnknown
UIPresentationController
UIPopoverPresentationController繼承自UIPresentationController沼溜,這個對象的作用官方文檔說是:一個為彈出的視圖控制器提供高級視圖和轉場管理的對象平挑。對于UIPresentationController更相近的內容限于篇幅,不再介紹系草,可以查看文檔UIPresentationController
定制我們想要的popopver效果(實現(xiàn))
先來說一下通熄,如果我們像上文官方提供的例子的話,運行的結果將是全屏找都,這可能是系統(tǒng)會幫我們自適應彈出的樣式唇辨,原因如下:
-
UIPopoverPresentationController
繼承自UIPresentationController
,在源代碼里能耻,可以看到這個類包含一個屬性
// By default this implementation defers to the delegate, if one exists, or returns the current presentation style. UIFormSheetPresentationController, and
// UIPopoverPresentationController override this implementation to return UIModalPresentationStyleFullscreen if the delegate does not provide an
// implementation for adaptivePresentationStyleForPresentationController:
open var adaptivePresentationStyle: UIModalPresentationStyle { get }
通過類型可以看出這個屬性就是設置彈出popover的樣式赏枚,再看一下注釋亡驰,發(fā)現(xiàn)默認是根據(jù)UIAdaptivePresentationControllerDelegate實現(xiàn)的,如果實現(xiàn)了自適應樣式的方法饿幅,那么這個屬性的值就是設為響應的值凡辱。如果它的協(xié)議沒有在adaptivePresentationStyleForPresentationController方法中實現(xiàn)的話,在UIFormSheetPresentationController和UIPopoverPresentationController重寫了這個實現(xiàn)栗恩,并且返回UIModalPresentationStyleFullscreen透乾。這就說明了,為什么官方的例子彈出的效果是全屏的了
-
UIPopoverPresentationController
的delegate
遵守的協(xié)議為UIPopoverPresentationControllerDelegate
磕秤,這個協(xié)議又繼承自UIAdaptivePresentationControllerDelegate
乳乌。在Xcode中查看這個協(xié)議的內容,會發(fā)現(xiàn)這個協(xié)議可以控制自適應彈出的樣式市咆。并且從下圖的第一個方法的注釋中可以看出,自適應的樣式只支持UIModalPresentationFullScreen和UIModalPresentationOverFullScreen钦扭,并沒有我們想要的popover
另外,我們注意到第二個方法的注釋:返回UIModalPresentationNone指示不會發(fā)生自適應的樣式床绪,經(jīng)過測試,果真如此其弊。如果在這個代理方法返回UIModalPresentationNone癞己,我們將可以定制我們自己的彈出樣式,這也是本文所展示的效果的解決方法梭伐。同樣痹雅,如果在第一個代理返回返回UIModalPresentationNone的話,也可以禁止彈出的自適應效果
了解了思路以后糊识,用代碼來寫出我們想要的效果
-
Swift實現(xiàn)
- 基于UIBarButtonItem
let vc: UIViewController = UIViewController() @IBAction func itemPop(_ sender: UIBarButtonItem) { vc.view.backgroundColor = UIColor.orange // 設置彈出的尺寸 vc.preferredContentSize = CGSize(width: 150, height: 150) // 設置彈出的樣式 vc.modalPresentationStyle = UIModalPresentationStyle.popover // 這個屬性為true時表明除了popover內部绩社,其它地方不響應事件,也就是說點擊其他地方popover不會消失 // 這個屬性默認為false赂苗,也就是點擊popover周圍的地方會消失(狀態(tài)欄除外) // modalInPopover is set on the view controller when you wish to force the popover hosting the view controller into modal behavior. // When this is active, the popover will ignore events outside of its bounds until this is set to NO. vc.isModalInPopover = true // 獲取UIPopoverPresentationController對象 let popoverPresentationController = vc.popoverPresentationController // 設置popoverPresentationController顯示的錨點 popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem // 設置代理 popoverPresentationController?.delegate = self // 設置方向 popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.any self.present(vc, animated: true, completion: nil) } // 方法一: func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle { return .none } // 方法二: // func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle { // return .none // }
效果圖如本文開篇圖片效果
- 基于父類是UIView的控件
let vc: UIViewController = UIViewController() @IBAction func buttonPop(_ btn : UIButton) { vc.preferredContentSize = CGSize(width: 150, height: 150) vc.modalPresentationStyle = UIModalPresentationStyle.popover let popoverPresentationController = vc.popoverPresentationController popoverPresentationController?.sourceView = btn popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.up popoverPresentationController?.delegate = self popoverPresentationController?.backgroundColor = UIColor.purple self.present(vc, animated: true, completion: nil) } // 方法一: func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle { return .none } // 方法二: // func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle { // return .none // }
效果圖如下
我們發(fā)現(xiàn)并不是我們想要的效果愉耙,popover的位置好像不對。調整它的位置拌滋,這時候就要用到sourceRect屬性了朴沿。沒有設置的情況下默認都是0,所以顯示在了按鈕的左上角(0败砂,0赌渣,0,0)的位置昌犹,下面設置一下sourceRect屬性
popoverPresentationController?.sourceRect = CGRect(x: btn.frame.size.width / 2, y: btn.frame.size.height, width: 0, height: 0)
效果圖如下
-
OC實現(xiàn)
- 基于UIBarButtonItem
- (IBAction)itemPop:(id)item { _vc.view.backgroundColor = [UIColor orangeColor]; _vc.preferredContentSize = CGSizeMake(150, 150); _vc.modalPresentationStyle = UIModalPresentationPopover; UIPopoverPresentationController *popoverPresentationController = _vc.popoverPresentationController; popoverPresentationController.barButtonItem = item; popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionAny; popoverPresentationController.delegate = self; [self presentViewController:_vc animated:YES completion:nil]; } #pragma mark - UIPopoverPresentationControllerDelegate - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller { return UIModalPresentationNone; } //- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { // return UIModalPresentationNone; //}
效果和前文一樣
- 基于父類是UIView的控件
- (IBAction)popBtnClick:(UIButton *)btn { _vc.preferredContentSize = CGSizeMake(150, 150); _vc.modalPresentationStyle = UIModalPresentationPopover; UIPopoverPresentationController *popoverPresentationController = _vc.popoverPresentationController; popoverPresentationController.sourceView = btn; popoverPresentationController.sourceRect = CGRectMake(btn.frame.size.width / 2, btn.frame.size.height, 0, 0); popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionUp; popoverPresentationController.delegate = self; popoverPresentationController.backgroundColor = [UIColor purpleColor]; [self presentViewController:_vc animated:YES completion:nil]; } #pragma mark - UIPopoverPresentationControllerDelegate - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller { return UIModalPresentationNone; } //- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { // return UIModalPresentationNone; //}
遺留問題:當?shù)谝淮吸c擊陰影使popover消失的時候坚芜,會報一個警告,可以點擊這里進行了解
[Warning] <_UIPopoverBackgroundVisualEffectView 0x7f99f4607860> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
參考文獻:
UIPopoverPresentationController
UIPopoverController的使用
如果不想用系統(tǒng)的樣式斜姥,也可以自定義鸿竖,通過使用popoverBackgroundViewClass這個屬性來自定義背景沧竟,具體可以參考這篇文章Customizing UIPopover with UIPopoverBackgroundView