Edited at

コピペで使えるPopover(swift5.0)大全


はじめに

iOSのPopoverについて調べたまとめです。(swift5.0)


iPhoneで表示

iPhoneでのPopover表示です。なんとなくセルタップ時にセルからPopoverを表示してみました。


ViewController.swift

extension ViewController: UITableViewDelegate {

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) else {
return
}
let viewController = PopoverViewController() //popoverで表示するViewController
viewController.modalPresentationStyle = .popover
viewController.preferredContentSize = viewController.view.frame.size

let presentationController = viewController.popoverPresentationController
presentationController?.delegate = self
presentationController?.permittedArrowDirections = .up
presentationController?.sourceView = cell
presentationController?.sourceRect = cell.bounds

present(viewController, animated: true, completion: nil)
}
}

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


デリゲートの実装は注意しましょう!!(iPhone 横向きでポップオーバー表示するとこわれた\(^o^)/)


ナビゲーションのbarButtonItemから表示

navigationItembarButtonItemからのPopover表示です。


ViewController.swift

@objc func showPopover(_ sender: UIBarButtonItem) {

let viewController = PopoverViewController() //popoverで表示するViewController
viewController.modalPresentationStyle = .popover
viewController.preferredContentSize = viewController.view.frame.size

let presentationController = viewController.popoverPresentationController
presentationController?.delegate = self
presentationController?.permittedArrowDirections = .up
presentationController?.barButtonItem = sender

present(viewController, animated: true, completion: nil)
}



Popoverを閉じないようにする

Popoverは通常枠外をタップすると閉じますが、枠外をタップしても閉じないようにします。


ViewController.swift

extension ViewController: UIPopoverPresentationControllerDelegate {

func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool {
return false
}
}


Popoverの吹き出しの色設定

popoverの吹き出しに色を設定します。


ViewController.swift

extension ViewController: UITableViewDelegate {

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) else {
return
}
let viewController = PopoverViewController() //popoverで表示するViewController
viewController.modalPresentationStyle = .popover
viewController.preferredContentSize = viewController.view.frame.size

let presentationController = viewController.popoverPresentationController
presentationController?.delegate = self
presentationController?.permittedArrowDirections = .up
presentationController?.sourceView = cell
presentationController?.sourceRect = cell.bounds
presentationController?.backgroundColor = .red

present(viewController, animated: true, completion: nil)
}
}



Popoverの背面のViewのジェスチャの有効化

Popoverの背面のViewのジェスチャを有効化します。背面のtableViewのジェスチャを有効化しました。passthroughViewsにtableViewを指定しているのでtableViewをスクロールやタップしてもPopoverは閉じません。navigationBarをタップするとPopoverは閉じます(tableViewの範囲外)。


ViewController.swift

extension ViewController: UITableViewDelegate {

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) else {
return
}
let viewController = PopoverViewController() //popoverで表示するViewController
viewController.modalPresentationStyle = .popover
viewController.preferredContentSize = viewController.view.frame.size

let presentationController = viewController.popoverPresentationController
presentationController?.delegate = self
presentationController?.permittedArrowDirections = .up
presentationController?.sourceView = cell
presentationController?.sourceRect = cell.bounds
presentationController?.passthroughViews = [tableView]

present(viewController, animated: true, completion: nil)
}
}



おまけ無限Popover


PopoverからPopoverを表示する

表示したPopoverからさらにPopoverを表示します。単純にPopoverのViewControllerのボタン押下時にPopoverを表示しているだけです。


PopoverViewController.swift

@IBAction func showPopover(_ sender: UIButton) {

let viewController = PopoverViewController()
viewController.modalPresentationStyle = .popover
viewController.preferredContentSize = viewController.view.frame.size

let presentationController = viewController.popoverPresentationController
presentationController?.delegate = self
presentationController?.permittedArrowDirections = .up
presentationController?.sourceView = sender
presentationController?.sourceRect = sender.bounds

present(viewController, animated: true, completion: nil)
}



Popoverの背面のViewからPopoverを表示する

Popoverの背面のViewのジェスチャの有効化で背面のtableViewのジェスチャを有効化しましたがPopover表示中にセルをタップしても下記のようなエラーが出てPopoverが表示されません。

Warning: Attempt to present <PopoverTest.PopoverViewController: 0x7fb4ff613e90>  on <PopoverTest.ViewController: 0x7fb4ff507d10> which is already presenting (null)

ちょっといじってPopoverの上にPopoverを表示するようにします。


ViewController.swift

extension ViewController: UITableViewDelegate {

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) else {
return
}
let viewController = PopoverViewController() //popoverで表示するViewController
viewController.modalPresentationStyle = .popover
viewController.preferredContentSize = viewController.view.frame.size

let presentationController = viewController.popoverPresentationController
presentationController?.delegate = self
presentationController?.permittedArrowDirections = .up
presentationController?.sourceView = cell
presentationController?.sourceRect = cell.bounds
presentationController?.passthroughViews = [tableView]

topViewController.present(viewController, animated: true, completion: nil)
}

var topViewController: UIViewController {
var target: UIViewController = self
while let presentedViewController = target.presentedViewController {
target = presentedViewController
}
return target
}
}



さいごに

passthroughViewsなんかあったんだ!!という驚きからこの記事を書いてみました。意外と古くiOS8.0からあったみたいですね。(公式ドキュメント

色々書きましたが1個目2個目以外はあまり使う機会がないと思います