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

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

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

<img src="https://user-images.githubusercontent.com/34936885/64709396-21283200-d4f1-11e9-9dc3-246bb9eb6e66.png" width="300px" />

```swift: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^)／](https://qiita.com/am10/items/8224cd24c7ba3c5ae96c))

# ナビゲーションのbarButtonItemから表示
`navigationItem`の`barButtonItem`からのPopover表示です。

<img src="https://user-images.githubusercontent.com/34936885/64709355-0fdf2580-d4f1-11e9-8e63-cd2dfb4e1c50.png" width="300px" />

```swift: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は通常枠外をタップすると閉じますが、枠外をタップしても閉じないようにします。

```swift:ViewController.swift
extension ViewController: UIPopoverPresentationControllerDelegate {
    func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool {
        return false
    }
}
```

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

<img src="https://user-images.githubusercontent.com/34936885/64709265-f047fd00-d4f0-11e9-8b0b-2826d0df4b41.png" width="300px" />

```swift: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の範囲外）。

<img src="https://user-images.githubusercontent.com/34936885/64710020-38b3ea80-d4f2-11e9-8ffd-35dbbf20d27d.gif" width="300px" />

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

<img src="https://user-images.githubusercontent.com/34936885/64709216-de665a00-d4f0-11e9-9392-77814737791a.png" width="300px" />

```swift: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のジェスチャの有効化](#popoverの背面のviewのジェスチャの有効化)で背面のtableViewのジェスチャを有効化しましたがPopover表示中にセルをタップしても下記のようなエラーが出てPopoverが表示されません。

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

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

<img src="https://user-images.githubusercontent.com/34936885/64709142-ba0a7d80-d4f0-11e9-90b4-57c9917732c1.png" width="300px" />

```swift: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からあったみたいですね。（[公式ドキュメント](https://developer.apple.com/documentation/uikit/uipopoverpresentationcontroller/1622312-passthroughviews)）

色々書きましたが[1個目](#iphoneで表示)と[2個目](#ナビゲーションのbarbuttonitemから表示)以外はあまり使う機会がないと思います:neutral_face: