画面遷移時にUINavigationBarが勝手に表示される現象への対処法
Table of Contents
NavigationBarが非表示になっているViewControllerから、SwiftUIを含む別のViewControllerへ遷移するとき、NavigationBarが勝手に表示されてしまう現象に遭遇しました。
これに対処しようとして、navigationController?.navigationBar.isHidden = true
や navigationController?.setNavigationBarHidden(true, animated: false)
を呼んでも、全く解決しませんでした。
この現象を回避するために、今回は、UIHostingController
を継承したカスタマイズしたクラスを実装して対応しました。
現象の詳細 #
- NavigationBarを非表示にしている画面(画面A)から別の画面(画面B)へpush遷移するとき、NavigationBarが表示済みになった画面Bがスライドしてくる
- 画面Bは、UIHostingControllerでSwiftUIを流し込んでいる
- UIHostingControllerを使わない画面では、この問題は発生しない
- 以下の処理を呼んでいても、NavigationBarが表示された画面がスライドしてくる
navigationController?.navigationBar.isHidden = true
navigationController?.setNavigationBarHidden(true, animated: false)
navigationBarHidden(true)
(SwiftUIのViewで)
この現象が発生する条件は、よくわかっていません。 発生するアプリと、発生しないアプリがあります。同じXcodeのバージョンを使っていてもです。
勝手にNavigationBarが表示されてしまう原因 #
UIHostingControllerが、その内部処理でNavigationBarのisHidden
をfalse
にしているようです。
また、そのタイミングは viewWillAppear
ということがわかりました。
対処・実装 #
UIHostingControllerを継承したカスタムクラスを用意して、viewWillAppear
の挙動を変更しました。
final class UIHostingControllerCustomized <Content>: UIHostingController<AnyView> where Content : View {
private let preferredNavigationBarHidden: Bool
public init(preferredNavigationBarHidden: Bool, rootView: Content) {
self.preferredNavigationBarHidden = preferredNavigationBarHidden
super.init(rootView: AnyView(rootView.navigationBarHidden(preferredNavigationBarHidden)))
}
@objc required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // ← ここで isHiddenが強制的にfalseになることがある
// 意図した設定値になっていない場合は、
// `isHidden`の値をセットし直す
if navigationController?.isNavigationBarHidden != preferredNavigationBarHidden {
navigationController?.navigationBar.isHidden = preferredNavigationBarHidden
navigationController?.setNavigationBarHidden(preferredNavigationBarHidden, animated: false)
}
}
}
このクラスを、いつものUIHostingControllerと同じように使うことで、この現象を回避することができました。
// ViewController側の実装
override func viewDidLoad() {
super.viewDidLoad()
let vc = UIHostingControllerCustomized(preferredNavigationBarHidden: true,
rootView: MyView())
addChild(vc)
vc.view.frame = view.bounds
view.addSubview(vc.view)
vc.didMove(toParent: self)
}
参考にしたページ #
- UINavigationControllerをカスタマイズ 〜OSの影響を受けづらいカスタムナビゲーションの実装〜 - クックパッド開発者ブログ
- Coockpadのアプリでも同様の現象に遭遇していたようです
- Coockpadでは、カスタムしたUINavigationControllerを使っているので、NVC側で対応したようです
- swift - Unable to hide the navigationBar when embedding swiftUI in UIkit - Stack Overflow
- 今回の対応で実装したコードは、こちらにある回答がベースになっています