iOS Storyboard 和 UIColor 创建出来的颜色显示效果不同

路漫漫其修远兮,吾将上下而求索。
真是绝了,这个问题从我开始搞 iOS 开发时就一直困扰这我了,虽然那时候我也尝试这找解决方案,但是一直都没能找到怎么解决,只是大概知道是配色文件到诊的,但是要如果更改正确却无从下手了。
现在看来,我找不到解决方案也是理所应当的了,至于原因接着往下看。(提示:我搞 iOS 开发那会还是 iOS8 时代)。

你有没有遇到过以下问题:

  • 设计师给你的颜色值在 Interface Builder 中设置以及代码设置与真机与模拟器显示的颜色不一致,甚至肉眼完全看的出来。
  • 为一个普通的控件设置 Color,设置了颜色为 XXXXXX。取色工具测的也是正常的 XXXXXX,但是模拟器 / 真机运行起来之后,颜色变色了,用取色工具测,颜色变成了 YYYYYY。
  • 为什么当我用颜色吸管工具在 Photoshop 选择一个颜色的和 RGB 值,在 Xcode 中设置后得不到相同的颜色?
  • 为什么真机或者模拟器截图后,用吸管取色后得到的值与 Interface Builder 中设置的颜色值不一样?
  • 太恶心了,就一个颜色为什么不一样?
  • 我做错了什么吗?

看完这篇文章后,这些问题将会全部消失。

color space

色彩空间,色域。我不会在这里详细介绍, 进一步了解可以阅读: http://www.dpbestflow.org/color/color-space-and-color-profiles 在那里, 你可以找到关于色彩模型, 色彩空间和色彩配置的定义, 以获得更深的理解。我也会稍后再博客中翻译整篇文章。

color profile

色彩配置。我不会在这里详细介绍, 进一步了解可以阅读: http://www.dpbestflow.org/color/color-space-and-color-profiles 在那里, 你可以找到关于色彩模型, 色彩空间和色彩配置的定义, 以获得更深的理解。我也会稍后再博客中翻译整篇文章。

理解苹果的颜色处理

苹果认为相同的 RGB 值在任何地方都应该显示相同。其实应用一个 RGB 的组合在不同的设备上不会总是有着相同的颜色,这取决于你使用的是哪一个颜色配置(color profile)。

需要知道是,color profile 是展现一个色彩空间中颜色的数值模型 (色彩空间, 色域(color space)是展现颜色的一个方式, 例如: RGB, CMYK, HSV, 等等),一些色彩配置(color profile)是“设备相关” 的,一些色彩配置(color profile)是 “设备无关” 的。这意味着同样的颜色在不同的设备 (“设备无关”) 上会展现相同的颜色,其中一些将会根据设备的特点改变颜色(“设备相关”)

同样有趣的是, 当你截图时, 不仅每个像素的 RGB 值都得到了存储, 而且还有关于被截取的设备的互补信息。这样, 苹果可以通过计算不同的 RGB 组合来使颜色在不同的设备中看起来相同, 以最佳的方式使这些颜色与设备的特性和限制相匹配。

说了这么多, 给定一个色彩空间(color space) (例如, RGB), 您将在其中有多个色彩配置 (一般 RGB、Adobe RGB、PAL/SECAM 等), 因此你将有多种方法使用不同的 RGB 组合来获得相同的颜色。

Xcode 中的 RGB 色彩配置(color profile)有 Adobe RGB、Apple RGB、Device RGB(设备 RGB)、Generic RGB(通用 RGB)、Wide Gamut RGB(广域 RGB)。要查看整个色彩配置列表, 可以从 Xcode 的 Interface Builder 的色彩选择工具中查看。

因此, 当您选择选择了 RGB 值组合,并更改了要使用的色彩配置时, 您将获得相同的颜色,但是会得出不同的 RGB 值,这是应用颜色时使开发人员非常恼火的主要问题。

例如, 通用 RGB (10、80、105) 和设备 RGB (0、99、124) 是相同的颜色,但有不同的 RGB 值。如你所见。这就是为什么最终可以有不同的颜色, 即使你使用的是你从别处取色的精确的 RGB 组合 (Photoshop 吸管, 数码测色计(Digital Color Meter)等)。

同样, 如果在不同的色彩配置中使用相同的 RGB 组合, 则会得到不同的颜色。

此外, Photoshop 处理颜色时,使用 Photoshop 的人与使用 RGB 值的人不是同一台设备不说,Adobe 使用人员 Photoshop 的色彩空间也未必与开发人员一致 (可能会是 Adobe 发明的颜色空间), 所以当你试图直接从 Photoshop 中选取它们并将其应用于 Xcode 时, 情况可能会变得更糟。

Photoshop 在编辑菜单的颜色设置中可以查看更改色彩配置。

所有这一切都解释了为什么当你使用数码测色计选择一个颜色, 你可能会得到不同的 RGB 值但是是相同的颜色。如果你在不同的显示器 (因为额外的设备信息可以添加到每个像素, 当你选择颜色, 取决于您使用的是与设备相关的色彩配置还是与设备无关的。

你可以猜到,我们要使用一个设备无关的, 如 sRGB (代表标准的 RGB), 所以无论我们将在什么地方显示,我们将得到相同的 RGB 值时。

iOS 中代码自定义设置 RGB 与 Interface Builder 自定义设置 RGB 颜色一致。都使用了 sRGB 色彩配置。

在 XIB 中的色彩配置

选择颜色后使用 sublime 等文本编辑器直接打开 storyboard 或者 xib 文件。即可看到如下几种结果。

如果色彩配置采用了 sRGB ,sb/xib 的 xml 中会这样写,colorSpace=”custom” customColorSpace=”sRGB” ,一般都是使用颜色选择器自定义 RGB 后自动选择的 sRGB 色彩配置

如果色彩配置采用了 Display P3, sb/xib 的 xml 中会这样写,colorSpace=”custom” customColorSpace=”displayP3”,

如果色彩配置采用了 Generic RGB, sb/xib 的 xml 中会这样写,colorSpace=”calibratedRGB” ,一般都是使用颜色选择器自定义 RGB 后手动选择的 Generic RGB 色彩配置

如果色彩配置采用了 Generic Gray , sb/xib 的 xml 中会这样写,colorSpace=”calibratedWhite”,一般都是系统默认的灰白颜色。

如果色彩配置采用了 Device Gray , sb/xib 的 xml 中会这样写,colorSpace=”deviceRGB”,,一般都是使用颜色选择器自定义 RGB 后手动选择的 DeviceRGB 色彩配置

如果色彩配置采用了 Adobe RGB , sb/xib 的 xml 中会这样写,colorSpace=”adobeRGB1998”,,一般都是使用颜色选择器自定义 RGB 后手动选择的 Adobe RGB 色彩配置

最后发现,手动选择的 Apple RGB 色彩配置,sb/xib 的 xml 中会这样写,colorSpace=”sRGB”,说明苹果默认统一成了 sRGB.

在纯代码中的色彩配置

最新 Xcode8 测试, 以下代码使用 sRGB 色彩配置

1
+ (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha

类似 [UIColor darkGrayColor] 使用 Generic Gray 色彩配置

不信我们 log 下

1
2
3
4
5
6
7
CGColorSpaceRef ref = CGColorGetColorSpace([UIColor colorWithRed:104/255.0 green:104/255.0 blue:104/255.0 alpha:1].CGColor);
NSLog(@"%@",ref);
CGColorSpaceRef ref2 = CGColorGetColorSpace([UIColor darkGrayColor].CGColor);
NSLog(@"%@",ref2);
<CGColorSpace 0x60000003f2a0> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)
<CGColorSpace 0x60800002af20> (kCGColorSpaceICCBased; kCGColorSpaceModelMonochrome; Generic Gray Gamma 2.2 Profile; extended range)

颜色拾取与应用

既然你明白了这件事的 ‘原因’, 让我们来谈谈技巧。

要选择颜色, 我们将使用苹果系统自带的 “数码测色计” 应用,你可以在系统的实用工具或者 Spotlight 找到。这个程序适用于一些色彩配置计算屏幕上的一个像素的 RGB 值。

Xcode 的颜色选择器也支持拾取,但是不能设置指定色彩配置的颜色拾取。只能是 sRGB(代表标准的 RGB)。

要应用颜色,我们将使用 Xcode 的 Interface Builder, 或者, 我们也可以以代码方式进行。

颜色拾取

打开数码测色计(Digital Color Meter),下拉中选择 “以 sRGB 显示”/ “Display in sRGB”, 使用取色吸管取色,shift+cmd+c,快捷键拷贝当前屏幕像素 RGB 的值.

如果想拷贝 16 进制颜色,在菜单的显示中可以设置切换。

然后再 Xcode 的 Interface Builder,颜色选择器中选择 sRGB(默认也是 sRGB),填入取的颜色 RGB 值。

或者代码设置

1
self.label.backgroundColor = [UIColor colorWithRed:39/255.0 green:45/255.0 blue:51/255.0 alpha:0];

所以,总结下技巧:

使用取色软件,以 sRGB 色彩配置,取得 RGB 值。

设计最好也使用 sRGB 色彩配置进行设计。

如果设计要给开发取色要使用 sRGB 取色。

因为代码初始化 UIColor 是不能像 NSColor 一样指定色彩空间的。所以要以不变(sRGB)应万变。

使用 sRGB 在 Xcode Interface Builder(或通过代码) 中应用这些 RGB 值。

参考资料

https://stackoverflow.com/questions/28367811/color-in-storyboard-not-matching-uicolor
http://www.skyfox.org/ios-app-color-set-and-color-profile.html