遷移元画面を透過させる場合に使用するUIModalPresentationStyle.overCurrentContextの注意点

## はじめに

遷移元画面を透過させる方法として、 `modalPresentationStyle` を `overCurrentContext` にする方法がよく知られています。

```swift
let modalViewController = ModalViewController()
modalViewController.view.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.6)
modalViewController.modalPresentationStyle = .overCurrentContext
present(modalViewController, animated: false)
```

基本的には、 この方法で問題ないのですが、 `UITabBarController` & `UINavigationController` を使用している場合、 `TabBar` が遷移先画面の上に来てしまいます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/44142/36505af0-c8a8-cbf2-03da-530bb620e67e.png" width=50%>
<img src="https://qiita-image-store.s3.amazonaws.com/0/44142/6fcf634f-5b36-d2a6-e365-7b7bafc360fa.png" width=80%>



`TabBar` を出し続けたい場合はこのままでもいいですが、透過させたい場合は、遷移先画面が `TabBar` の上に来て欲しいことの方が多いのではないでしょうか。


## 解決策

解決策を先に書いてしまいますが、 `modalPresentationStyle` に `overFullScreen` を指定することで解決できます。

```swift
let modalViewController = ModalViewController()
modalViewController.view.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.6)
// ここを overCurrentContext -> overFullScreen に変更
modalViewController.modalPresentationStyle = .overFullScreen
present(modalViewController, animated: false)
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/44142/5066dd2b-05d2-799a-ecb5-72302a20a4c5.png" width=50%>
<img src="https://qiita-image-store.s3.amazonaws.com/0/44142/06a56a94-f645-a4b6-1c94-a68166dbefb2.png" width=80%>


この方法で要件は満たせるのですが、実は `overCurrentContext` のままでもこの問題を回避する方法があります。

## UIModalPresentationStyle.overCurrentContextの仕様を追う

https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/overcurrentcontext

`UIModalPresentationStyle.overCurrentContext` のドキュメントの一文に

> Using this presentation style, the current view controller's content is displayed over the view controller whose definesPresentationContext property is true.

とあります。 
`definesPresentationContext` が `true` のViewController上に表示されるようです。

https://developer.apple.com/documentation/uikit/uiviewcontroller/1621456-definespresentationcontext

`definesPresentationContext` のドキュメントには、

> If no view controller defines the presentation context, UIKit asks the window’s root view controller to handle the presentation.

とあり、

> The default value for this property is false.

とあります。

そして最後に

> Some system-provided view controllers, such as UINavigationController, change the default value to true.

となっています。

デフォルト値は `false` なので基本的には、 `window’s root view controller` でpresentされるようですが、 `UINavigationController` は、 `definesPresentationContext` が `true` となるので、 `UINavigationController` でpresentされるようです。（ちなみに `UITabBarController` も `true`）

![Screen Shot 2019-04-06 at 14.39.31.png](https://qiita-image-store.s3.amazonaws.com/0/44142/703a4a52-e2b1-6ed2-bdd7-0fbfa8b9c106.png)


よって `overCurrentContext` を使用したまま `TabBar` の上に表示するには、 `UINavigationController` の `definesPresentationContext` を `false` にしてあげればいいわけです。

``` swift
let modalViewController = ModalViewController()
modalViewController.view.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.6)
modalViewController.modalPresentationStyle = .overCurrentContext
// 下の１行を追加
navigationController?.definesPresentationContext = false
present(modalViewController, animated: false)
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/44142/5066dd2b-05d2-799a-ecb5-72302a20a4c5.png" width=50%>
<img src="https://qiita-image-store.s3.amazonaws.com/0/44142/06a56a94-f645-a4b6-1c94-a68166dbefb2.png" width=80%>

これにより `window’s root view controller` （ここでは `UITabBarController`）からpresentされるようになります。

![Screen Shot 2019-04-06 at 14.38.42.png](https://qiita-image-store.s3.amazonaws.com/0/44142/ec9b3799-216a-6d82-328b-5f6aeecf325a.png)


`overFullScreen` は `window’s root view controller` からpresentされるようなので `TabBar` の上に表示されるわけですね。


## まとめ

ここまで説明してきましたが、要するに `UITabBarController` でpresentしてあげれば `TabBar` の上に表示させられます。
なので、 `tabBarController?.present` や `UIApplication.shared.keyWindow?.rootViewController?.present` でも実現できますが、拡張性や汎用性を考慮してこのような方法は極力取らない方がいいと思います。

先に書きましたが、この場合は `overFullScreen` を指定してあげる方法がもっともシンプルな方法だと思います。

## 余談
`UIPresentationController` を使用したモーダル表示の場合、 `modalPresentationStyle` に `custom` を指定しますが、 `custom` もroot view controllerからpresentされるのでtabBarの上に表示されます。