iOS13から導入されるダークモードへの対応方法

#はじめに
iOS13.0からダークモードが導入されます。

> Test your design in both light and dark appearances. See how your interface looks in both appearances, and adjust your designs as needed to accommodate each one. Designs that work well in one appearance might not work in the other.

Human Interface Guidelinesによると[上記のような記載](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/dark-mode/)があるように、全てのアプリで今後は特別な理由がない限りダークモードへの対応が必要になってくると思われます。

iOS13.0が正式にリリースされてから慌てて取りかかることが無いよう事前に準備をしようと思い、ダークモード対応について調べてみました。

※ 2019年7月20日現在のXcode11 beta4での情報になります。正式にリリースされた際に実際の挙動が異なる場合があるのでご注意ください。

## ダークモードに対応する大まかな手順
1. ダークモードとライトモードで使用するそれぞれの配色を考える
2. 画像を使用している場合はそれぞれのモードに対応した画像も用意する
3. AssetCatalog or UIColorにダークモード用とライトモード用の色や画像を定義する(画像はAsset Catalogのみ)
4. 上記3で定義した色や画像を各UIパーツに設定する

大まかな流れは上記の手順になるんじゃないかなと思います。

それぞれの手順についてもう少し詳しく説明していこうと思います。

## ダークモードとライトモードで使用するそれぞれの配色を考える
iOS12系までを対象にアプリを開発してきた場合は、iOS13のSDKで一度ビルドをしてみましょう。
ビルドしたアプリを、ダークモード設定にした端末で動かしてみるとダークモードの影響度が確認できます。

どうですか？

意外と影響はなかったでしょうか。
それとも真っ黒で使い物にならなかったでしょうか。

iOS13からは接頭辞に[systemがついたUIColor](https://developer.apple.com/documentation/uikit/uicolor/3173140-systembackgroundcolor?language=objc)が追加されました。

この色達は、端末の状況に応じて色を動的に変更してくれる色です。

この動的に変更される色が、デフォルトで使用されている？ので
色を明示的に設定していない箇所などはダークモードの影響を受けているのではないでしょうか。

いずれにせよ、ダークモードに対応されているアプリは存在しないのではないかと思います。

アプリへの影響度がわかったら、アプリ内の配色を考えましょう。

ここら辺は専門外なので説明できないのですが、[こちら](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/color/)なんかは目を通す価値はありそうです。

## Asset Catalogでダークモード用とライトモード用の色や画像を定義する場合
・Asset Catalogに新しく色の定義を追加(New Color set)
・追加した色の定義を選択し、Attributes Inspectorを選択
・AppearancesのAny,Darkを選択し適宜色を設定

上記の手順で設定できます。

## UIColorにダークモード用とライトモード用の色を定義する場合
iOS13.0以降のSDKに`init(dynamicProvider: @escaping (UITraitCollection) -> UIColor)`という`UIColor`のイニシャライザが追加されました。
このイニシャライザを使用することで、

```swift:UIColor+Original.swift
extension UIColor {
    @available(iOS 13.0, *)
    class var originalColor: UIColor {
        get {
            .init(dynamicProvider: { (traitCollection) -> UIColor in
                switch traitCollection.userInterfaceStyle {
                case .dark:
                    UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 1)
                case .light:
                    UIColor(displayP3Red: 255, green: 255, blue: 255, alpha: 1)
                case .unspecified:
                    UIColor(displayP3Red: 123, green: 123, blue: 123, alpha: 1)
                @unknown default: break
                }
            })
        }
    }
}

self.view.backgroundColor = .originalColor // これだけで両モードに対応可能
```

上記の様に`UIColor`を拡張してモード毎の色の定義を簡単に追加することができます

## 補足1 モードの管理について
ダークモード、ライトモードなどの設定は`UITraitCollection`が管理しています。
アプリ内で適用されるモードを取得したい場合は、

```swift:ViewController.swift
class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print(self.traitCollection.userInterfaceStyle)
    }
    
}
```

`UITraitCollection`の`useInterfaceStyle`というプロパティから取得することが可能です。

取得できる値は3パターンあります。

| 値 | 設定 |
|:-:|:-:|
|  unspecified |  デフォルト値(おそらく端末の設定に準拠) |
|  dark |  ダークモード |
|  light |  ライトモード |

## 補足2 アプリ内でのモードの設定について
iOS13のSDKでビルドをした場合、設定を何か追加しなくても端末がダークモードの場合アプリもダークモードで表示されます。
とりあえずライトモードを固定にしたい(あまりオススメはできなそうですが)場合は設定を追加することで実現できます。

アプリ内のモードを設定する方法は2パターンあります。

1. info.plistにUser Interface Styleを追加する
2. ViewControllerのoverrideUserInterfaceStyleプロパティを変更する

上記の2パターンのどちらかを使用すればアプリ内で設定を行えます。

### info.plistにUser Interface Styleを追加する
User Interfaceに追加できる値は3つあります。

| 値 | 設定内容 |
|:-:|:-:|
|  Automatic |  端末の設定に準拠 |
|  Dark |  ダークモード固定 |
|  Light |  ライトモード固定 |

上記以外の値(空も含む)は全てLightが適用されます。

### ViewControllerのoverrideUserInterfaceStyleプロパティを変更する
`ViewController`は`overrideUserInterfaceStyle`というプロパティを持っています。

```swift:ViewController.swift
class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.overrideUserInterfaceStyle = .light
    }
    
}
```

このプロパティを変更することで設定をViewController単位で行うことが出来ます。

### info.plistとViewControllerの両方を設定した場合
info.plistとViewControllerの両方を設定した場合はViewControllerの値が優先されます。(overrideしてるから当然と言えば当然)

思わぬバグに繋がることもあるので特別な場合を除いてはinfo.plistに設定しておくのが無難だと思います。


## 定義した色や画像を各UIパーツに設定する
最後にこれまでに定義してきた色や画像をアプリ内のUIパーツに設定していきましょう。

StoryBoard上で設定しても、コード上で設定してもどちらでも問題ないです。

各プロジェクトのルールに沿って実装してください。

## まとめ
現時点でダークモードの対応をしていないからといってリジェクトの対象になることはないと思いますが、
いつAppleさんの審査基準が変わるかはわからないのでなるべくダークモードとライトモードの両対応をしておきたいです。

いやあ、やっぱり新機能とか知らないことを調べるって楽しいですね。。

色々至らない点があると思うので、ここはこうとかこうした方がいいとか色々な意見をお待ちしております。

## 参考URL
・https://developer.apple.com/videos/play/wwdc2019/214/
・https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/dark-mode/
・https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/color/
・https://xiaozhuanlan.com/topic/1064789253