Edited at

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


はじめに

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

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 が遷移先画面の上に来てしまいます。



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


解決策

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

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)



この方法で要件は満たせるのですが、実は 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.


とあります。

definesPresentationContexttrue の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 は、 definesPresentationContexttrue となるので、 UINavigationController でpresentされるようです。(ちなみに UITabBarControllertrue

Screen Shot 2019-04-06 at 14.39.31.png

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

let modalViewController = ModalViewController()

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



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

Screen Shot 2019-04-06 at 14.38.42.png

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


まとめ

ここまで説明してきましたが、要するに UITabBarController でpresentしてあげれば TabBar の上に表示させられます。

なので、 tabBarController?.presentUIApplication.shared.keyWindow?.rootViewController?.present でも実現できますが、拡張性や汎用性を考慮してこのような方法は極力取らない方がいいと思います。

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


余談

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