はじめに
iOSのPopoverについて調べたまとめです。(swift5.0)
iPhoneで表示
iPhoneでのPopover表示です。なんとなくセルタップ時にセルからPopoverを表示してみました。

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から表示
navigationItem
のbarButtonItem
からのPopover表示です。

@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は通常枠外をタップすると閉じますが、枠外をタップしても閉じないようにします。
extension ViewController: UIPopoverPresentationControllerDelegate {
func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool {
return false
}
}
Popoverの吹き出しの色設定
popoverの吹き出しに色を設定します。

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の範囲外)。

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を表示しているだけです。

@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を表示するようにします。

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からあったみたいですね。(公式ドキュメント)