SwiftUIで全画面時にHomeIndicatorを隠したい
SwiftUIではUIViewControllerを直接制御しないため、HomeIndicatorを非表示にする際に、prefersHomeIndicatorAutoHiddenメソッドを設定することができません。今のところはSwiftUIのContentView上でもpreferenceに設定が無いようなので、自前のクラスを挟んで制御するようにしてみました。
記述を簡略化できました。
SceneDelegateの起動設定を確認
iOS13から導入されたSceneDelegateにより、プロジェクト内の構成が変わっており、内部的にはSceneDelegateクラスのscene(_ ,willConnectTo: 〜〜〜)メソッドでrootViewControllerを指定するようになっています。
これを以下のように切り替えるイメージです。
今回はホームバーの制御を目的にしていますが、UIViewControllerを利用したその他の設定にも活用できると思います。
設定の流れ
この起動の流れに対して、rootViewControllerにカスタムクラスを設定することでホームバーの非表示にしてみます。
- カスタムクラス(HomeIndicatorHiddenHostingController)の作成
- (カスタムクラスにホームバー制御のメソッドを追加)
- SceneDelegateでカスタムクラスを指定
基本的にこのような流れで設定します。
1. カスタムクラス(HomeIndicatorHiddenHostingController)の作成
もともと使用されているUIHostingControllerクラスを継承したカスタムクラスを作成します。
class HomeIndicatorHiddenHostingController<Content>: UIHostingController<Content> where Content: View {
override var prefersHomeIndicatorAutoHidden: Bool {
return true
}
}
2.(カスタムクラスにホームバー制御のメソッドを追加)
以下のメソッドの戻り値でホームバー(HomeIndicator)をHiddenに設定するようにしています。正確には、表示されなくなるわけではなく一定時間経つと見えなくなります(触れば再表示される)。
override var prefersHomeIndicatorAutoHidden: Bool {
return true
}
ここではhiddenに固定していますが、動的に設定を切り替えたい場合には、setNeedsUpdateOfHomeIndicatorAutoHidden()メソッドも利用する必要があります。
参考:https://dev.classmethod.jp/smartphone/iphone/iphone-x-dealing-with-home-indicator/
3. SceneDelegateでカスタムクラスを指定
SceneDelegateのrootViewController指定で、カスタムクラスを利用します。
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = HomeIndicatorHiddenHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
以上の流れで、SwiftUIのContentViewを表示している際にもホームバーが隠れるようになると思います。
未解決:UIHostingController継承時の書き方 → 解決済み
うまく調べきれず親クラスであるUIHostingControllerの初期化部分の指定をAnyViewとする方法しかわからなかったので、改善の余地があるかと思います。
Swift5.1から導入されたOpaque Result Type(where 句)を利用することで型を制限せずSwiftUIのViewプロトコルに準拠したいずれかのViewクラスをContentとしてラップしたもの、という表現をすることができました。初期化がごっそり減ったのでだいぶ使いやすいと思います。
【SwiftUI を理解するために必要な Swift 5.1 の新機能 (some View編)】
https://speakerdeck.com/kumamotone/swiftui-woli-jie-surutamenibi-yao-na-swift-5-dot-1-falsexin-ji-neng-some-viewbian?slide=41
【Opaque Result Typeの解説】
https://qiita.com/omochimetaru/items/f13fe3e54fab01648ba4
未解決時のコード
class HomeIndicatorHiddenHostingController: UIHostingController<AnyView> {
init<V: View>(rootView: V) {
super.init(rootView: AnyView(rootView))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override var prefersHomeIndicatorAutoHidden: Bool {
return true
}
}
解決済のコード
class HomeIndicatorHiddenHostingController<Content>: UIHostingController<Content> where Content: View {
override var prefersHomeIndicatorAutoHidden: Bool {
return true
}
}
参考
「How to hide the home indicator with SwiftUI?」
https://stackoverflow.com/questions/56795572/how-to-hide-the-home-indicator-with-swiftui
【随時更新】iPhoneX完全対応マニュアル
https://qiita.com/cokaholic/items/6a8ee3852c8ed28ea2aa