去年iOS 13の新機能で出たダークモードを自社アプリに対応したのですが、思わぬ挙動でバグが出てしまいそのTips共有です
traitCollectionDidChangeの罠
ライト⇄ダークの色切り替え時に特定の処理を行いたい場合は
func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
を使って切り替え時の検知ができます
単純にライト/ダーク時に専用の配色を入れる(例 CGColorで色を入れる必要がある)場合は問題ないですが、それ以外の何か別の設定を入れる(例 ダークモードの時だけ〇〇する)みたいなケースだと問題がおきます
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if UITraitCollection.current.userInterfaceStyle == .dark {
// ダーク時だけしたい処理
}
}
コントロールセンターのライト→ダークに切り替え時に traitCollectionDidChange
が呼ばれるのですが、 バックグラウンドに入った瞬間にtraitCollectionDidChange
が2回呼ばれます(ライト⇄ダーク切り替えなしに)
しかも、ただ2回呼ばれるのではなくバッググラウンドに入った時に、今端末がライト/ダークどっちの設定が入っているかを確認できる UITraitCollection.current.userInterfaceStyle
がライトとダークそれぞれの状態で来ます
本来ダークモード設定の時だけしたい処理がバックグラウンド時にライトとダークそれぞれ来ることになり、端末設定がライトモードの時でもダークモード時にしたい処理をしてしまう、期待外れなことが起きてしまいます😇
この挙動がよくわからず・・・
ちなみに他のライフサイクルでの検知できるメソッドだとどうか?
同様の問題が起きてダメでした・・
例
- viewWillLayoutSubviews
- viewDidLayoutSubviews
など
回避策
個人的にあんまり納得いく修正方法ではありませんが、バッググラウンド時は特定の処理までしないようにしました
バックグラウンド上で設定を変えてもフォアグラウンドに戻った時呼ばれるので問題ないです
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if UIApplication.shared.applicationState == .background {
return
}
if UITraitCollection.current.userInterfaceStyle == .dark {
// ダークの時のみしたい処理
}
}
なぜ今回の問題になった挙動をするかいまいちわかってないのでうーんっていう気持ちで修正できました()
おまけ traitCollectionDidChange
で色を入れるのは大丈夫なの?
大丈夫です
ライトモード時はライト、ダークモード時はダークの色を入れてくれます(最終的に)
試しにアセットカタログのファイルで以下の色を用意して
- 色
UITraitCollection.current.userInterfaceStyle
の要素をログで追って確認し、ライトモードでバッググラウンド時に入ってみて検証してみました
dark
Optional([1.0, 1.0, 1.0, 1.0])
light
Optional([0.0, 0.0, 0.0, 1.0])
2回呼ばれますが、
ライトモードだとダーク→ライト
の順番に呼ばれるので最終的にライトの色が入ります
同様にダークモードでも ライト→ダーク
と呼ばれダークの色が入るので大丈夫です