轉自:i‘m Allen的博客ios
先給代碼:https://github.com/allenhsu/CRNavigationControllergit
1. 問題的表現github
相信不少人在 iOS 7 的適配過程當中遇到了相似這樣的問題。當你試圖經過設置 navigationBar.barTintColor 的時候,你陷入了一個兩難的困境,假設你的預期是這樣的:app
設計稿ide
但當 navigationBar.translucent 爲 YES 時,你的 navigationBar 看起來多是這樣的:函數
實際狀況測試
最簡單的方法是妥協地設置 translucent 爲 NO,因而 navigationBar 的背景色看起來與設計稿一致了,可是設計師可能會跟你吐槽這個 navigationBar 一點也不 iOS 7,他但願 navBar 仍是能夠透出高斯模糊後的背景。ui
縱觀 App Store,網易新聞和 Facebook 彷佛比較好地解決了這個問題。設計
蒐集了一些相關資料後自覺找到了不錯的解決方案,因而總結成文。ip
2. iOS 7 中的 barTintColor
《Bar color calculator for iOS7 (UIToolbar and UINavigationBar)》給出了從設計稿色值到 UIColor 的轉換和逆轉換公式:
假設設計稿中色值爲 x(0-255),轉換到 UIColor 用的色值用以下公式:
y = (x - 102) / 0.6
若是取 0-1.0(UIColor 中 RGB 的取值),對應公式:
y = (x - 0.4) / 0.6
反之:
x = 0.6 * y + 0.4
這意味着只設置 barTintColor,你獲得的屏幕上結果的 RGB 值均不會小於 102(0.4),換言之,這樣的顏色明度較高,沒法覆蓋完整的色域。但假若你的設計稿是一個明度較高的背景色,如灰色 (102, 102, 102),那麼只需設置 barTintColor 爲純黑 (0, 0, 0) 就能獲得設計稿中所需的背景色,可是大多狀況下這個公式沒法知足需求。
注:本文所說的屏幕上的結果色值均指與白色底色疊加後的顏色,假設設計稿中色值亦是指白底疊加的狀況。
3. 不完美的解決方案
Stackoverflow 上有一個解決方案,做者也在 GitHub 附上了代碼。這個方案在我看來並不完美,最大的缺點就是代碼中輸入的色值並不是最終屏幕輸出的色值(仍以白底爲參考),仍有明顯色差。這就意味着若是要實現與設計稿一致的效果,你須要經過不斷地調整、取色、對比來找到一個 Magic Value。不過這個方案給出了一個很好的思路,即經過疊加一個半透明的 CALayer,來改變 navigationBar 的背景色,或者說提升背景色的明度。
4. 色彩疊加
爲了減少篇幅,這裏把色彩疊加簡化爲兩色疊加,假設 c1 是當前顏色,c2 爲覆蓋在 c1 上面的顏色,而且透明度(alpha/opacity)爲 a,那麼疊加後屏幕上的顏色爲 (1 - a) * c1 + a * c2。
5. 數學問題
剩下的工做就是純數學問題了,題目是:在 2 中計算所得的顏色上疊加什麼顏色和透明度的 Layer,能夠獲得設計稿中的原色。
假設設計稿色值爲 n,傳入 barTintColor 的參數也爲 n(我要求的所設即所得),根據 2 中公式,navigationBar 自帶的背景色爲:
b1 = 0.6 * n + 0.4
設疊加的 Layer 的背景色爲 n2,透明度爲 a,那麼疊加後獲得的顏色爲:
b2 = (1 - a) * b1 + a * n2
= (1 - a) * (0.6 * n + 0.4) + a * n2
我但願:
b2 = n
即:
(1 - a) * (0.6 * n + 0.4) + a * n2 = n
推導獲得:
n2 = 0.4 * n / a + 0. 6 * x + 0.4 - 0.4 / a
爲了保證 n2 > 0,能夠獲得:
a > (0.4 - 0.4 * n) / (0.6 * n + 0.4)
就是說爲了實現部分較深的顏色,a 不得不取較高的值,也就意味着 navBar 的通透度可能會適當下降,例如網易紅的例子中,a 大概在 0.78 左右,因此看起來不那麼通透。
該不等式的右邊是個遞減函數,因此取色值 RGB 中的最小值來計算右式最大值從而獲得 a 的最小值,該步對應代碼中的:
CGFloat minVal = MIN(MIN(red, green), blue);
if
([self convertValue:minVal withOpacity:opacity] < 0) {
opacity = [self minOpacityForValue:minVal];
}
在獲得 a 即 opacity 的值後,經過如下代碼計算獲得疊加的 Layer 的色值:
- (CGFloat)convertValue:(CGFloat)value withOpacity:(CGFloat)opacity
{
return
0.4 * value / opacity + 0.6 * value - 0.4 / opacity + 0.4;
}
綜上,這樣計算出來的疊加層的背景色和透明度可使得疊加後的結果正好與設計稿的色值一致,至此很好地解決了 iOS 7 中 navigationBar 的 barTintColor 的適配問題,該方法一樣適用於解決 UIToolBar 和 UITabBar 的背景色的適配。
最後附上 GitHub 地址:https://github.com/allenhsu/CRNavigationController 樣例中,Red 爲網易紅,Blue 爲臉書藍,自行測試效果。