在 iPhone & iPad 上顯示 popover 彈出視窗

在 iPad 上我們時常看到如上圖紅色框框的 popover 彈出視窗,它可以讓原本佔滿整個螢幕的 controller 畫面瘦身,變成只佔滿螢幕的一部分。這樣子的功能其實不是 iPad 獨有,iPhone 也做得到。接下來就讓我們一步步實現 popover 的效果吧。

新版做法: iOS 14 以上版本,包含 14

使用 Pull Down Button & Pop Up Button。

舊版做法: iOS 14 之前

在 iPad 上顯示 Popover

我們希望點選 + 後,選擇角色的表格以 popover 呈現。

就像魔法一樣,只要拉 segue 時選擇 Present As Popover 即可實現彈出視窗的效果。

在 iPhone 上顯示 Popover

然而同樣的程式,跑在 iPhone 上卻讓我們明白童話都是騙人的。按了 + 之後,表格還是佔滿整個螢幕。

其實 iPhone 還是能夠做到 popover,只是比較麻煩。我們還要加入以下程式。

1 將第一頁的 controller 設為 UIPopoverPresentationController 物件的 delegate。

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {   override func prepare(for segue: UIStoryboardSegue, sender: Any?) {         segue.destination.popoverPresentationController?.delegate = self   }

當我們以 popover 方式呈現第二頁的 table controller 時,此時將自動生成 UIPopoverPresentationController 物件幫忙控制 popover。而我們可透過定義 UIPopoverPresentationControllerDelegate 的 function 控制 popover。

因此我們讓第一頁的 controller 遵從 protocol UIPopoverPresentationControllerDelegate,然後在 function prepare 裡利用 segue.destination.popoverPresentationController 讀取 UIPopoverPresentationController 物件,然後將 controller 設成它的 delegate 。

2 定義 UIPopoverPresentationControllerDelegate 的 function adaptivePresentationStyle(for:traitCollection:)

func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {   return .none}

為了讓 iPhone 也能顯示 popover,我們必須在此 method 裡回傳 presentation style .none。

修改 Popover 的大小

修改 popover 顯示的 controller 的 preferredContentSize,即可控制 popover 的大小。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {   segue.destination.preferredContentSize = CGSize(width: 150, height: 200)   segue.destination.popoverPresentationController?.delegate = self}

從程式 present controller 實現 popover 的效果。

我們也可以不透過 segue,從程式 present controller 實現 popover 的效果。

以 iPad App 為例:

@IBAction func buttonPressed(_ sender: Any) {   if let controller = storyboard?.instantiateViewController(withIdentifier: "TableViewController") {     controller.modalPresentationStyle = .popover     controller.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem     present(controller, animated: true, completion: nil)   }}

說明:

1 controller.modalPresentationStyle = .popover

以 popover 的效果呈現。

2 controller.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem

讓 popover 的箭頭指到 rightBarButtonItem。

讓 popover 的箭頭指到某個特定的 view。

剛剛我們透過 barButtonItem 指定 popover 的箭頭指到 bar 上的 button,但我們也可利用 sourceView 讓它指到某個特定的 view。

@IBAction func buttonPressed(_ sender: Any) {   if let controller = storyboard?.instantiateViewController(withIdentifier: "TableViewController") {      controller.modalPresentationStyle = .popover      controller.popoverPresentationController?.sourceView = eyeSwitch      present(controller, animated: true, completion: nil)   }}

如上圖所示,現在箭頭完美地指到 switch 開關。可惜它有個小小的缺點,它指到了 switch 的左上方,因為 popover 預設會指到 sourceView 的左上方。

沒關係,只要設定 UIPopoverPresentationController 的 sourceRect,即可控制箭頭指到的位置。

controller.popoverPresentationController?.sourceRect = CGRect(origin: .zero, size: eyeSwitch.frame.size)

從 segue 控制 popover 箭頭指到的對象。

從 segue 也可以設定 popover 箭頭指到的對象。如下圖所示,Anchor 欄位代表箭頭指到的對象,因此從 Anchor 右邊的空心圓圈拉線到我們希望箭頭指到的 view 即可。

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com