麦芽を支える技術

麦芽(ばくが、英語:malt)とは、麦、特に大麦の種子を発芽させたもので、ビール、ウイスキー、水飴の原料となる。(Wikipediaより)

iPhoneXから表示されるHome Indicatorの表示/非表示をViewController毎に設定したい

ちょいとこのHome Indicatorを隠したい要件があって、まだ世の中にHome Indicatorの記事出回ってないようなのでメモ。

Home Indicatorとは

iPhoneXの画面下部に表示されるバー。この辺のiPhoneX画像見てもらうとありますよねー。

https://developer.apple.com/ios/human-interface-guidelines/overview/iphone-x/

iPhoneXからは物理ホームボタンがなくなって、画面下部からのスワイプ操作でホーム画面に戻すので、その操作を連想させるための画面コントロールらしい。

UIViewControllerでの表示制御方法

Home Indicatorの表示制御はUIViewControllerの以下の2つのメソッドで行う。

func prefersHomeIndicatorAutoHidden() -> Bool
  • そのViewControllerでのHome Indicator表示/非表示を定めるメソッド。デフォルトはfalse(Home Indicator表示させる)
func childViewControllerForHomeIndicatorAutoHidden() -> UIViewController?
  • そのViewControllerでのHome Indicator表示/非表示を 子のViewControllerに委託 するメソッド。デフォルトはnil
  • nilを返却すると、表示/非表示は自ViewController内のprefersHomeIndicatorAutoHidden()の値に従う
  • 子ViewControllerを返却すると、表示/非表示は返却したViewController内のprefersHomeIndicatorAutoHidden()の値に従う

基本的には任意のViewControllerでprefersHomeIndicatorAutoHidden()をオーバーライドし、そこで任意のBool値を返却することで表示制御を行うこととなる。

コンテナ型ViewController使用時の注意点

複数のViewControllerを統合して1つのUIとして形成されているコンテナ型のViewController(ex. UINavigationController, UITabBarController, UISplitViewController, etc...)の場合、Home Indicatorの表示制御は最初に親となるViewControllerに委託される。

親ViewControllerで特に何も設定していない(デフォルト値の)場合、子ViewControllerでの表示制御は親ViewControllerの設定が引き継がれるため、Home Indicatorは「表示」設定となり、子ViewControllerでprefersHomeIndicatorAutoHidden()true(非表示)に設定してもこのメソッド自体呼ばれない。

子ViewControllerにHome Indicatorの表示制御を委託

子ViewControllerにHome Indicatorの表示制御を委託したい(≒Home Indicatorの表示/非表示を子ViewController毎に決めたい)場合は、以下のように設定する。

例)UINavigationControllerを使用中の場合

親ViewController側の設定

// ExtensionでUINavigationControllerに一律設定する場合
extension UINavigationController {
    override func childViewControllerForHomeIndicatorAutoHidden() -> UIViewController? {
        // 表示制御を委託したいViewController(この例では現在NavBar表示中のViewController)を指定
        return self.visibleViewController
    }
}
// 既にUINavigationControllerを継承したカスタムクラスがある場合
class CustomNavigationController: UINavigationController {

    ...

    override func childViewControllerForHomeIndicatorAutoHidden() -> UIViewController? {
        return self.visibleViewController 
    }
}

子ViewController側の設定

class ChildViewController: UIViewController {

    ...

    override func prefersHomeIndicatorAutoHidden() -> Bool {
        return true   // true: 非表示, false: 表示
    }
}

ちなみに親子関係がもっと深い場合は、その分だけ子への表示制御委託を繰り返す必要があるみたい。

おわりに

今時はTabBarやNavigationBar使ってるアプリがほとんどだと思うんで、非表示にする必要があるならこの委託処理は必須なのでは。

あ、ちなみに「委託」という言葉はなんとなくイメージで使っているだけで、この言葉で合ってるのかは不明。「移譲」だとデリゲートみたいに聞こえちゃうので。