SwiftUI 全画面でホームバーを隠す

#SwiftUIで全画面時にHomeIndicatorを隠したい

SwiftUIではUIViewControllerを直接制御しないため、HomeIndicatorを非表示にする際に、prefersHomeIndicatorAutoHiddenメソッドを設定することができません。今のところはSwiftUIのContentView上でもpreferenceに設定が無いようなので、自前のクラスを挟んで制御するようにしてみました。

__記述を簡略化できました。__


##SceneDelegateの起動設定を確認
iOS13から導入されたSceneDelegateにより、プロジェクト内の構成が変わっており、内部的にはSceneDelegateクラスのscene(_ ,willConnectTo: 〜〜〜)メソッドでrootViewControllerを指定するようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59249/8cb5ccef-da0a-5842-418b-c30b7c497ad0.png)

これを以下のように切り替えるイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59249/c8375a79-e55a-06f9-92e9-1b0f3d196266.png)


今回はホームバーの制御を目的にしていますが、UIViewControllerを利用したその他の設定にも活用できると思います。

##設定の流れ

この起動の流れに対して、rootViewControllerにカスタムクラスを設定することでホームバーの非表示にしてみます。

1. カスタムクラス（HomeIndicatorHiddenHostingController）の作成
1. （カスタムクラスにホームバー制御のメソッドを追加）
1. SceneDelegateでカスタムクラスを指定

基本的にこのような流れで設定します。


##1. カスタムクラス（HomeIndicatorHiddenHostingController）の作成

もともと使用されているUIHostingControllerクラスを継承したカスタムクラスを作成します。


```swift:HomeIndicatorHiddenHostingController.swift
class HomeIndicatorHiddenHostingController<Content>: UIHostingController<Content> where Content: View  {
    override var prefersHomeIndicatorAutoHidden: Bool {
        return true
    }
}
```

##2.（カスタムクラスにホームバー制御のメソッドを追加）

以下のメソッドの戻り値でホームバー（HomeIndicator）をHiddenに設定するようにしています。正確には、表示されなくなるわけではなく一定時間経つと見えなくなります（触れば再表示される）。

```swift:init
    override var prefersHomeIndicatorAutoHidden: Bool {
        return true
    }
```

>ここではhiddenに固定していますが、動的に設定を切り替えたい場合には、setNeedsUpdateOfHomeIndicatorAutoHidden()メソッドも利用する必要があります。
参考：https://dev.classmethod.jp/smartphone/iphone/iphone-x-dealing-with-home-indicator/


##3. SceneDelegateでカスタムクラスを指定
SceneDelegateのrootViewController指定で、カスタムクラスを利用します。

```swift:SceneDelegate.swift
    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



##未解決時のコード
```swift:HomeIndicatorHiddenHostingController.swift
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
    }
}
```

##解決済のコード
```swift:HomeIndicatorHiddenHostingController.swift
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