🦄

iOS 18 からの画面サイズの変化を含めたUITraitCollectionへの対応方法

2024/06/28に公開

昨年、iOS 17からの画面サイズの変化への対応に関する記事を書きました。
https://zenn.dev/matsuei/articles/a9143244622d01
そこでは registerForTraitChanges(_:handler:) を利用することが推奨されていましたが、 iOS 18 から別の方法が推奨されるようになりました。
この記事では、画面サイズの変化を含めた UITraitCollection の変化にどう対応するかをまとめていきます。

AutoTraitTracking

iOS 18 から AutoTraitTracking という仕組みが導入され、これを使うことが推奨されています。
https://developer.apple.com/documentation/uikit/app_and_environment/automatic_trait_tracking

AutoTraitTracking は、上記のドキュメントに記載の関数内で UITraitCollection へのアクセスがあると、その要素が変化すると自動的にその関数が呼ばれる仕組みです。
実際にどのような動きになるかを具体的なコードで見ていきます。

例えば以下のようなコードでは UITraitCollection の変化だけでは viewWillLayoutSubviews() は呼ばれません。

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
}

一方で以下のようなコードにすると、端末のライトモード・ダークモードの切り替えで viewWillLayoutSubviews() が呼ばれます。

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    switch traitCollection.userInterfaceStyle {
    case .unspecified:
    case .light:
        // ライトモードのレイアウト
    case .dark:
        // ダークモードのレイアウト
    }
}

つまり、上記のドキュメントに記載のメソッド内で UITraitCollection に応じたレイアウト更新を書いておくことで、自動的に UITraitCollection の更新に合わせた対応が可能になります。

パフォーマンス的な話

今までと異なり、実装者が意識することはかなり減らせるようになっていました。
また、今回の記事を書くにあたり実際の挙動を確認すると viewWillLayoutSubviews() など AutoTraitTracking の対象になっているメソッドの実行回数が最適化されていることが分かりました。

例えば、ライトモード・ダークモードの切り替え時、viewWillLayoutSubviews()
iOS 17 では複数回呼ばれ
iOS 18 では traitCollection.userInterfaceStyle へのアクセスが無ければ呼ばれない
という状況でした。

AutoTraitTracking の導入によってこれらの関数でレイアウトの更新を行うようになるため、最適化されているのでは無いかと予想しています。

Discussion