実践 iOS13ダークモード対応

[個人アプリ](https://apps.apple.com/jp/app/id1460380962)をダークモード対応したので、その具体的な実装の流れとtipsについてまとめました。
※すでにDark Modeの概要や実装については知っている前提です。まだの方は文末の参考記事をご参照ください

## Color Setを活用する
基本的にはハードコードしていた色指定をDynamic Colorに置き換える作業になります。

今まで色の指定はコードで定義していたのですが、ダークモード対応をきっかけにAssets CatalogのColor Set(iOS11~)に移行しました。

Color Setのメリットは以下のとおりです。

* Storyboard Xib, codeで色が使える
* Light, Darkの色指定が可能

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/b47ca032-3ef8-c766-0517-2a4db88e43df.png)

ちなみにiOS13SDKのUIKitにプリセットされてるUI Element Colors (`.systemBackgroundColor`など)も指定できます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/89bad96c-8446-3068-248f-1e4750460228.png)

Asset Catalogを新規作成し、画像のAssetsと色のAssetsは分けて管理すると見やすい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/021d0552-e227-ae24-4fc4-8264f8015da1.png)

[SwiftGen](https://github.com/SwiftGen/SwiftGen)などを使って、コードから参照する時にタイプセーフになるよう対応するとさらに便利。

``` yaml
#swiftgen.yml
xcassets:
  inputs:
   - ./FamilyReminder/Images.xcassets
   - ./FamilyReminder/Colors.xcassets
  outputs:
    - templateName: swift5
      output: ./FamilyReminder/Generated/Assets.swift
```

``` swift
// 利用する時はこんな感じでタイプセーフに使える
closeButton.tintColor = Asset.Colors.primary.color
```

二重で管理する必要がなくなったので、すべての色指定をColor Assetsに移行し終わったタイミングで既存のコードでColor指定していたファイルは破棄しました。

Storyboard, Xib, codeで使えるし、ダークモード対応するなら、まずColor Assetに移行すると後の作業が捗ると思います。

## UI Element Colorsをなるべく使う
iOS13SDKにプリセットされている、UI Element Colors(`.systemBackgroundColor`など)を使うと自動で下記の対応をしてくれます。

- **色を指定するviewがmodalyかどうかで濃さを変えてくれる**
- iOS13以前の端末の場合、代替の色を指定してくれる(Color Set同様)
  - `.systemBackgroundColor` の場合、whiteなど

純正のアプリをダークモードで見るとmodalは背景色が通常のviewの背景色より明るいことに気づきます。
これは1画面ずつ実装してるのではなく、UIKitが用意してくれている`.systemBackgroundColor` などが階層構造を把握して自動で処理してくれます。Viewがbase levelなのかelevated levelなのかで濃さが変わります。
(`.secondarySystemBackground` も `.systemBackgroundColor` の色に合わせて良しなにやってくれます)


| view    | modal    |
| :------------- | :------------- |
|  <img width="375" alt="cal.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/3b0f0165-57d7-036b-6c96-36d0ac21af80.png">      |      <img width="375" alt="cal_modal.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/51bd079e-587c-301a-f7c0-6f4c9a15fe0e.png">|
|  <img width="375" alt="reminder.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/71759cde-a2d9-2794-5c9a-8c4dd4f440a2.png">      |      <img width="375" alt="reminder_modal.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/0991b776-8897-6e24-b91a-30b55b4103e1.png">|


この機能がかなり便利なので、可能な限り、`.systemBackgroundColor` などを使った方が良いと感じました。ダークモード対応に必要な機能はすでに用意してくれているのでそれを活用したほうが良いです。
backgroundColorをカスタマイズしているアプリも多いと思うので、デザイナーとのコミュニケーションが必要な箇所でしょう。Appleが提供しているツールについて理解度を合わせることが大事でしょう。
僕はこれを機にTableViewが主体の画面などで独自に指定していた背景色を廃止し、 `.systemBackgroundColor` に統一しました。

## ダーク/ライトの表示確認
### Storyboard, Xibを使うと確認がラク
Storyboard, Xibを使ってレイアウトしている場合、IB上でダーク・ライトの簡易的な確認がビルドなしで出来ます。(IBの下のメニュー)

<img width="1088" alt="スクリーンショット 2019-09-15 18.40.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/daec3351-bfe8-d6b2-66f4-638c65c43131.png">

ビルド後は、Xcodeのメニューでリアルタイムに変更できます（実機/シミュレータ）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/526498ef-0b1b-cac8-c2b6-8621d9395366.png)

実機だとコントロールセンターにボタンを置くとパッと切り替えられます
`設定 > コントロールセンター > コントロールセンターをカスタマイズ`
![IMG_E6F7C0E3F2E7-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/0d5e7e58-ef91-9ef7-6878-fb563ca27e6b.jpeg)

## 微調整する
同僚のデザイナーに教えてもらった[Contrast Checker](https://contrastchecker.com/)で色の見分けやすさなど確認。
数値で判断できるので便利。
最終的にプライマリー色とdisabled色を少しビビットに微調整

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/1e8e8be3-7402-4a4c-e65f-c9f7ef2963d4.png)

作業完了までの時間は半日ちょっとくらい。画面数が20前後で自前でデザインも作ってるので、割と早くできたのだと思います。
むしろ色の指定をColor Setにするなどセマンティックな色指定にリファクタリングする作業に時間がかかりました。ダークモードは自分のアプリがセマンティックに色指定できているかどうか見つめ直す良い機会だと思います。

Color Set, UI Element ColorなどXcode上でセマンティックに色指定するツールは揃っているので、
業務でも全画面のデザインが必要？と言われると割と疑問で、主要な画面のデザインカンプがあれば、簡単にダークモード対応するツールは揃っているので直接IB.コードをいじってデザイナーと確認を取った方が早いと思います。

## 追記
スプラッシュ画面のダークモード対応を忘れないようにしましょう。アプリ立ち上げ直後の画面だけ一瞬白くなります。
僕はスプラッシュ画面特にカスタマイズしておらず放置していたのもあり、問題発覚がリリース後になりました😇（[Slackも忘れてたぽく、後追いリリースしてました](https://twitter.com/hirothings/status/1181556290075516928?s=20)）

対応は、LaunchScreen.storyboardのviewの背景色をSystem Background Colorに切り替えるだけです。

## 完成 :tada:

| light | dark    |
| :------------- | :------------- |
|  <img width="375" alt="IMG_1302.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/419768e9-ab95-f9b9-285a-3ade098b1d0b.png">     |    <img width="375" alt="IMG_1301.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30423/78baade8-2862-1a82-b6fa-5cf7830b6b82.png">|

今回ダークモード対応したアプリはこちらになります💁‍♂️
ぜひ触ってみてもらえると嬉しいです！

**[「ファミリーTODO」](https://apps.apple.com/jp/app/id1460380962)**


家族・カップルで共有できるTODOアプリです。
LINEなどで家族を招待するだけでTODOを共有できます。



## 参考記事
公式ドキュメント

* [Implementing Dark Mode on iOS (WWDC)](https://developer.apple.com/videos/play/wwdc2019/214/)
* [Supporting Dark Mode in Your Interface](https://developer.apple.com/documentation/xcode/supporting_dark_mode_in_your_interface)
* [HIG - Dark Mode](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/dark-mode/)

参考になった記事・スライド

* [Dark Mode / 20190617 #wwdc_rusuban](https://speakerdeck.com/takasek/20190617-number-wwdc-rusuban)
* [iOS 13からのダークモード対応のコツ](https://qiita.com/gonsee/items/c04b73787730c0e831df)