[SwiftUI] Color.red は 赤 じゃない

# 赤 だけど 真っ赤 じゃない

じゃあ、何色でしょうか？ 。。。　答え、`UIColor.systemRed`色です。



![scr2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/c8d32f3f-5df8-eb0a-6c63-bb0a6c614484.png)

ご存知のとおり、`赤`のRGB値は`(R: 255, G: 0, B: 0)`です。`UIColor.red`がこれに該当します。この色を`真っ赤`と呼ぶことにします。

これに対し、`Color.red`は`(R: 255, G: 59, B: 48)`で、`真っ赤`より少し暗めです。この色は、`UIColor.systemRed`と同じ色なんです。

上のスクショの真ん中が`真っ赤`。上下の赤丸と比べるとホンの少し明るさに違いがあることが分かります。


<br>

では、`Color.red`に`saturation`モデファイアで、もっと暗くした色のRGB値はなんでしょうか？

# Color.red.saturation(0.6) のRGB値は？

<br>![scr4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/21d5c62f-271d-e19c-0bdc-6e3409010e85.png)

実は`Color`は`View`なので、（`View`に）色の設定はできますが 現在の色情報（RGBやHUE）を取得する手段は提供されていません。なので、RGB値は不明なんです。



そこで、[`saturation`モデファイア](https://developer.apple.com/documentation/swiftui/view/saturation(_:)/)の処理を真似た `UIColor`のエクステンションを作成しました。（`UIColor`であれば、RGB値を簡単に求められますから）

（SwiftUIはソースコードが公開されていませんので、）試行錯誤のうえ、ようやく出来たので、ここにあげます。

> [`Color`](https://developer.apple.com/documentation/swiftui/color)ではなく[`UIColor`](https://developer.apple.com/documentation/uikit/uicolor/)のエクステンションとしたのは、SwiftUI以外でも使うためです。
（`UIColor`から`Color`に変換するのは`Color(uiColor)`で済みますから）
ネイティブMacAppの場合は、`UIColor`を`NSColor`に変える


# UIColor.saturation(_:) の実装

コードは以下のとおりです。

簡単に説明すると、元の色のグレースケール色を取得して、指定された割合に応じて グレースケール色を混ぜています。
>（真っ白と真っ黒 以外の）どんな色でも`.saturation(0)`にすると、グレーになることがヒントとなりました。

```swift
extension UIColor {
    func saturation( _ amount: CGFloat) -> UIColor {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0
        guard self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) else { return self }

      //let grayscale = 0.2126 * red + 0.7252 * green + 0.0722 * blue
        let grayscale = 0.2989 * red + 0.587 * green + 0.114 * blue

        let r = red * amount    + grayscale * (1.0 - amount)
        let g = green * amount  + grayscale * (1.0 - amount)
        let b = blue * amount   + grayscale * (1.0 - amount)
        
        return UIColor(red: r, green: g, blue: b, alpha: alpha)
    }
}
```


![scr5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/1efdd640-603d-b24f-bf5b-397f27fe56b5.png)

上のスクショの3つ目の赤丸が、当エクステンションで作成した色です。
一つ上の赤丸と同じ色に見えますね。


# 色の検証

`Color.saturation`モデファイアにどれだけ近いかを、以下のコードで比較検証します。

- saturation値を 1.0 から 5段階で 0.2 づつ小さくして横に並べる
- 色は、`Color.red`, `.green`, `.blue`, `.white`, `.black` の5色で試し、
上段が`Color.saturation`で、下段が 当エクステンションで作成した色

RGB値で比較すると、1〜2の違いがあるものもありますが、見た目は`同じ色`に見えます。

![scr3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/c920c1f7-7a3b-edc0-3e4e-3c8ad241a64d.png)



```swift:検証コード
    let colors = [Color.red, .green, .blue, .white, .black]
    ForEach(colors, id:\.self) { color in
        HStack {
            ForEach(0 ..< 6) {
                let amount = Double(5 - $0) * 0.2
                ZStack {
                    Rectangle().frame(height: 80)
                    color.saturation(amount)
                        .frame(width: 60, height: 60)
                        .overlay(alignment: .bottom) {
                            Text("\(amount * 100, specifier: "%.0f")%")
                                .foregroundColor(Color.yellow)
                        }
                }
            }
        }
        HStack {
            ForEach(0 ..< 6) {
                let amount = Double(5 - $0) * 0.2
                ZStack {
                    Rectangle().frame(height: 80)
                    Color(UIColor(color).saturation(amount))
                        .frame(width: 60, height: 60)
                        .overlay(alignment: .bottom) {
                            Text("\(amount * 100, specifier: "%.0f")%")
                                .foregroundColor(Color.yellow)
                        }
                }
            }
        }
    }

```

おそらく、RGB値の1〜2の違いは、グレースケール値を求めるときの係数の差だと想定します。
見た目で同じ色に見えるので、100％一致までは追求していません。

なお、`View`におけるRGB値は、画面上でスポイトを使って調べた値です。
（UIColorは`getRed(_:green:blue:alpha:)`で取得出来ます）

![scr6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/1613e8ab-a1b6-d97e-6f5b-d794e95539c6.png)