その日は突然訪れた
Important
Supporting Dark Mode is strongly encouraged. Use the UIUserInterfaceStyle key to opt out only temporarily while you work on improvements to your app's Dark Mode support.
Choosing a Specific Interface Style for Your iOS Appより
- Atrae Advent Calendar 2019 13日目を担当する中途1年目でGreen事業でiOSアプリエンジニアをしている米田です。今日は13日の金曜日ですね。そんな日にふさわしいダークな話題をご提供します。
伝説のデザイナーとの対話
-
iOS13 SDKでは標準でダークモードの対応がなされているとみなされ、何も対応を行わないとユーザーがダークモードで利用している場合に画面の色が意図せず変更されてしまいます。ちなみに暫定策として_Info.plist_のUIUserStyleInterfaceのキーにlightを指定し回避することもできます。
-
さて、このダークモード対応について社内でも議論となったのですが、「ちくしょう、転職だ。」の産みの親であるGreenの伝説のメインデザイナーと話し_「他でダークモードに対応しているアプリもある中で対応していないのはかっこ悪い」_という理由で対応をすることとなりました。このときの僕の心は新しいことに挑戦できるという全力ピカピカライトモードでした。このときまでは。。。
あれ、そんなに時間かかんないじゃん。
- 元々Greenのアプリでは下のようにColorの対応表Constantsを作り、UIColorをhex値(16進法)で指定する方法を採用していました。
struct ColorConstant {
static let TEXT_DISABLED = "BBBBBB"
static let BUTTON_DISABLED = "ECECEC" ......
}
hogeTextView = UIColor(hex: ColorConstant.TEXT_DISABLED)
-
これがあったので一旦伝説のデザイナーにColorの今の対応表を渡し、Darkmodeの場合の対応のColorを作ってもらうこととしました。そこは流石の伝説のデザイナー、1日とかからず対応表を作ってきてくれました。
-
そしてその対応表をもとに色々と調査し実装を始めました。初めに僕はviewControllerのtraitCollectionのプロパティのuserInterfaceStyleでDarkmode/Lightmodeの判定ができたのでOnMemoryにisDarkmodeというグローバルで呼び出せる変数をアプリの立ち上げ時に保存し、先ほどの色対応表をもとにDarkmode/Lightmodeかで出し分けることとしました。
// この方法はよくないですよ。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window.rootViewController = rootViewController
window.makeKeyAndVisible()
if #available(iOS 13.0, *) {
if rootViewController.traitCollection.userInterfaceStyle == .dark {
ApplicationContext.setDarkMode()
}
} else {
print("this is not over iOS 13.0")
}
// この方法はよくないですよ。
struct ColorConstant
static let TEXT_BASE = ApplicationContext.isDarkmode ? "ECECEC" : "484848"
static let TEXT_DISABLED = ApplicationContext.isDarkmode ? "737373" : "BBBBBB".....
}
- そして実際にテストしてみたところ想像していたよりも良い感じで、_「あれ思ったよりもいい感じじゃん、ほぼ対応完了!ダークモード?楽勝やん!」_とライトモードマックスなピカピカな気分で帰宅しました。
そしてダークサイドに堕ちた
-
そして翌日、伝説のデザイナーにチェックしてもらいました。すると僕の目では全く気づくことができなかった数々のおかしな点の大量フィードバックを受けました。ちょっと心がダークに染まりつつありました。
*ちなみに伝説のデザイナーはサウナ好きの優しい性格の方です。サウナ好きに悪い人がいるわけありませんよね。 -
そして自分でも改めて触りながら直すべきポイントをまとめていきました。
1. アプリ起動中にモードを切り替えられるとうまく色の切り替えができない。
- これは完全に僕の調査不足だっただけでした。下の記事を参考にさせていただき、UIColorのExtensionを以下のように作りすぐに対応できました。
iOS 13からのダークモード対応のコツ
extension UIColor{
private class func dynamicColor(light: UIColor, dark: UIColor) -> UIColor {
if #available(iOS 13, *) {
return UIColor { (traitCollection) -> UIColor in
if traitCollection.userInterfaceStyle == .dark {
return dark
} else {
return light
}
}
}
return light
}
public static var TEXT_BASE: UIColor {
return dynamicColor(light: UIColor(hex: "484848"), dark: UIColor(hex: "ECECEC"))
}
.....
}
2. 黒い写真の画像の上の白文字などLightmode/Darkmodeでもどちらでも同じ色にさせたい場合の対応。
- 写真の画像などはLightmode/Darkmodeでも変わるわけがなく、その上に載せている文字は常に同じ白にさせる等の対応が必要となりました。どこの部分を常に同じ色にするべきかを確認する作業はほぼ全画面を見ることとなり、まさに苦行でしかなかったです。これで僕の心は完全にダークサイドに落ちていきました。
- ちなみに常に同じ色にする場合は下のようにLight/Darkmodeともに同じhex値にして対応しました。
public static var WHITE: UIColor {
return dynamicColor(light: UIColor(hex: "FFFFFF"), dark: UIColor(hex: "FFFFFF"))
}
3. 画像を動的に変更できない。
- これも1と同じような問題ですが、アプリを起動しているときにダークモードに切り替えられてしまうと画像が動的に変わらないという問題がありました。
*ちなみにこの画像の左のキャラクターの名前は***「ぐりお」***です。伝説のデザイナーが名付け親です。
- こちらも色々と調べて下の記事を参考にさせていただき、viewWillLayoutSubviews()でモードの切り替えを検知することができたので、これを利用し動的に画像を変更しました。
iOS13ダークモード対応
4. その他、無限個の細かな調整。
-
Darkmodeにおいて色の対応表で作成されている色以外で個別に色を変えたいという部分が大量に出てきたので(例えばAのLabelとBのラベルは同じBASE_COLORを使っているが画面的にBのラベルのDarkmode時の色は違う色にしたいなど)、その度に個別にUIColorのExtension内で個別に動的なUIColorを作成しました。
-
細かな作業が苦手な僕は完全にダークサイドに堕ちて脳死しながら作業を続けました。
-
作業の合間にサウナに行き、水風呂で整ってフォースを使える気分にもなったりしました。***これこそダークモードとライトモードの切り替えだ!***とわけわかんないことを考えてました。
終焉、そして君は永遠に~Darkmode Forever~
-
上記のような作業をして、なんとか気合いで***ダークモードの対応を終わらせました。***多分サウナが無かったら完全にダークサイドに堕ちていたことでしょう。ただ今後も新たな機能を作るたびにライトモードだけではなくダークモードも意識しながら開発しなければなりません。
-
そして、例えばキャンペーンの画像を入れ込む際にはライトモードだけでなく、ダークモード用の画像をデザイナーの方に用意してもらう必要がありデザイナーの負荷も上がります。
-
これからも一生ダークモードとうまく付き合っていかねばならないことを考えるとまた少しダークサイドに堕ちかけますが、サウナで整って調整します。サウナは万能薬。
おまけ
-
弊社では僕や伝説のデザイナーと働きたい(できればサウナ好きな)エンジニアを大募集しております!
アトラエ iOSエンジニア -
明日は僕と同じマンションに住むYentaのバックエンドエンジニアの土屋がお届けします!