官方文檔告訴你UIPopoverPresentationController怎么用

何為UIPopoverPresentationController?
An object that manages the display of content in a popover.--一個用來管理在一個彈出窗口中展示內容的對象

先來看一下效果

使用UIPopoverPresentationController的效果

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透乾。這就說明了,為什么官方的例子彈出的效果是全屏的了

  • UIPopoverPresentationControllerdelegate遵守的協(xié)議為UIPopoverPresentationControllerDelegate磕秤,這個協(xié)議又繼承自UIAdaptivePresentationControllerDelegate乳乌。在Xcode中查看這個協(xié)議的內容,會發(fā)現(xiàn)這個協(xié)議可以控制自適應彈出的樣式市咆。并且從下圖的第一個方法的注釋中可以看出,自適應的樣式只支持UIModalPresentationFullScreenUIModalPresentationOverFullScreen钦扭,并沒有我們想要的popover
UIAdaptivePresentationControllerDelegate

另外,我們注意到第二個方法的注釋:返回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
    //    }
    

效果圖如下

基于父類是UIView的控件1

我們發(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)

效果圖如下

基于父類是UIView的控件2
  • 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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市千贯,隨后出現(xiàn)的幾起案子屯仗,更是在濱河造成了極大的恐慌,老刑警劉巖搔谴,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魁袜,死亡現(xiàn)場離奇詭異,居然都是意外死亡敦第,警方通過查閱死者的電腦和手機峰弹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芜果,“玉大人鞠呈,你說我怎么就攤上這事∮壹兀” “怎么了蚁吝?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長舀射。 經(jīng)常有香客問我窘茁,道長,這世上最難降的妖魔是什么脆烟? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任山林,我火速辦了婚禮,結果婚禮上邢羔,老公的妹妹穿的比我還像新娘驼抹。我一直安慰自己,他們只是感情好拜鹤,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布框冀。 她就那樣靜靜地躺著,像睡著了一般敏簿。 火紅的嫁衣襯著肌膚如雪左驾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天极谊,我揣著相機與錄音诡右,去河邊找鬼。 笑死轻猖,一個胖子當著我的面吹牛帆吻,可吹牛的內容都是我干的。 我是一名探鬼主播咙边,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼猜煮,長吁一口氣:“原來是場噩夢啊……” “哼次员!你這毒婦竟也來了?” 一聲冷哼從身側響起王带,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤淑蔚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后愕撰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刹衫,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年搞挣,在試婚紗的時候發(fā)現(xiàn)自己被綠了带迟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡囱桨,死狀恐怖仓犬,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情舍肠,我是刑警寧澤搀继,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站翠语,受9級特大地震影響律歼,放射性物質發(fā)生泄漏。R本人自食惡果不足惜啡专,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望制圈。 院中可真熱鬧们童,春花似錦、人聲如沸鲸鹦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馋嗜。三九已至齐板,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間葛菇,已是汗流浹背甘磨。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留眯停,地道東北人济舆。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像莺债,于是被迫代替她去往敵國和親滋觉。 傳聞我的和親對象是個殘疾皇子签夭,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內容