iOS開發(fā)筆記--使用blend改變圖片顏色

最近對(duì)Core Animation和Core

Graphics的內(nèi)容東西比較感興趣,自己之前也在這塊相對(duì)薄弱练般,趁此機(jī)會(huì)也想補(bǔ)習(xí)一下這塊的內(nèi)容,所以之后幾篇可能都會(huì)是對(duì)CA和CG學(xué)習(xí)的記錄的文章。

在應(yīng)用里一個(gè)很常見的需求是主題變換:同樣的圖標(biāo),同樣的素材日熬,但是需要按照用戶喜愛變?yōu)椴煌念伾T趇OS5和6的SDK里部分標(biāo)準(zhǔn)控件引入了tintColor肾胯,來滿足個(gè)性化界面的需求竖席,但是Apple在這方面還遠(yuǎn)遠(yuǎn)做的不夠。一是現(xiàn)在用默認(rèn)控件根本難以做出界面優(yōu)秀的應(yīng)用敬肚,二是tintColor所覆蓋的并不夠全面毕荐,在很多情況下開發(fā)者都無法使用其來完成個(gè)性化定義。解決辦法是什么艳馒?最簡單當(dāng)然是拜托設(shè)計(jì)師大大出圖憎亚,想要藍(lán)色主題?那好,開PS蓋個(gè)藍(lán)色圖層出一套藍(lán)色的UI第美;又想加粉色UI蝶锋,那好,再出一套粉色的圖然后導(dǎo)入Xcode什往。代碼上的話根據(jù)顏色需求使用image-blue或者image-pink這樣的名字來加載圖片牲览。

如果有一丁點(diǎn)重構(gòu)的意識(shí),就會(huì)知道這不是一個(gè)很好的解決方案恶守。工程中存在大量的冗余和重復(fù)(就算你要狡辯這些圖片顏色不同不算重復(fù)第献,你也會(huì)在內(nèi)心里知道這種狡辯是多么無力),這是非常致命的兔港。想象一下如果你有10套主題界面庸毫,先不論應(yīng)用的體積會(huì)膨脹到多少,光是想做一點(diǎn)修改就會(huì)痛苦萬分衫樊,比如希望改一下某個(gè)按鈕的形狀飒赃,很好,設(shè)計(jì)師大大請(qǐng)重復(fù)地修改10遍科侈,并出10套UI载佳,然后一系列的重命名,文件移動(dòng)和導(dǎo)入…一場災(zāi)難臀栈。

當(dāng)然有其他辦法蔫慧,因?yàn)檎f白了就是tint不同的顏色到圖片上而已,如果我們能實(shí)現(xiàn)改變UIImage的顏色权薯,那我們就只需要一套UI姑躲,然后用代碼來改變UI的顏色就可以了,生活有木有一下光明起來呀盟蚣。嗯黍析,讓我們先從一張圖片開始吧~下面是一張帶有alpha通道的圖片,原始顏色是純的灰色(當(dāng)然什么顏色都可以屎开,只不過我這個(gè)人現(xiàn)在暫時(shí)比較喜歡灰色而已)阐枣。

我們將用blending給這張圖片加上另一個(gè)純色作為tint,并保持原來的alpha通道奄抽。用Core Graphics來做的話蔼两,大概的想法很直接:

創(chuàng)建一個(gè)上下文用以畫新的圖片

將新的tintColor設(shè)置為填充顏色

將原圖片畫在創(chuàng)建的上下文中,并用新的填充色著色(注意保持alpha通道)

從當(dāng)前上下文中取得圖片并返回

最麻煩的部分可能就是保持alpha通道了如孝。UIImage的文檔中提供了使用blend繪圖的方法drawInRect:blendMode:alpha:宪哩,rect和alpha都沒什么問題,但是blendMode是個(gè)啥玩意兒啊…繼續(xù)看文檔第晰,關(guān)于CGBlendMode的文檔锁孟,里面有一大堆看不懂的枚舉值彬祖,比如這樣:

[objc]view plaincopy

kCGBlendModeDestinationOver

R?=?S*(1-?Da)?+?D

Available?in?iOS2.0and?later.

Declared?in?CGContext.h.

完全不懂..直接看之后的Discussion部分:

The blend mode constants introduced in OS X v10.5 represent the Porter-Duff blend modes. The symbols in the equations for these blend modes are:

R is the premultiplied result

S is the source color, and includes alpha

D is the destination color, and includes alpha

Ra, Sa, and Da are the alpha components of R, S, and D

原來如此,R表示結(jié)果品抽,S表示包含alpha的原色储笑,D表示包含alpha的目標(biāo)色,Ra圆恤,Sa和Da分別是三個(gè)的alpha突倍。明白了這些以后,就可以開始尋找我們所需要的blend模式了盆昙。相信你可以和我一樣羽历,很快找到這個(gè)模式:

[objc]view plaincopy

kCGBlendModeDestinationIn

R?=?D*Sa

Available?in?iOS2.0and?later.

Declared?in?CGContext.h.

結(jié)果 = 目標(biāo)色和原色透明度的加成,看起來正式所需要的淡喜。啦啦啦秕磷,還等什么呢,開始動(dòng)手實(shí)現(xiàn)看看對(duì)不對(duì)吧~

為了以后使用方便炼团,當(dāng)然是祭出Category澎嚣,先創(chuàng)建一個(gè)UIImage的類別:

[objc]view plaincopy

//??UIImage+Tint.h

#import?

@interfaceUIImage?(Tint)

-?(UIImage*)imageWithTintColor:(UIColor*)tintColor;

@end

暫時(shí)先這樣,當(dāng)然我們也可以創(chuàng)建一個(gè)類方法直接完成從bundle讀取圖片然后加tintColor瘟芝,但是很多時(shí)候并不如上面一個(gè)實(shí)例方法方便(比如想要從非bundle的地方獲取圖片)易桃,這個(gè)問題之后再說。那么就按照之前設(shè)想的步驟來實(shí)現(xiàn)吧:

[objc]view plaincopy

//??UIImage+Tint.m

#import?"UIImage+Tint.h"

@implementationUIImage?(Tint)

-?(UIImage*)imageWithTintColor:(UIColor*)tintColor

{

//We?want?to?keep?alpha,?set?opaque?to?NO;?Use?0.0f?for?scale?to?use?the?scale?factor?of?the?device’s?main?screen.

UIGraphicsBeginImageContextWithOptions(self.size,NO,0.0f);

[tintColorsetFill];

CGRect?bounds?=?CGRectMake(0,0,self.size.width,self.size.height);

UIRectFill(bounds);

//Draw?the?tinted?image?in?context

[selfdrawInRect:boundsblendMode:kCGBlendModeDestinationInalpha:1.0f];

UIImage*tintedImage?=?UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

returntintedImage;

}

@end

簡單明了锌俱,沒有任何難點(diǎn)晤郑。測試之:[[UIImage

imageNamed:@"image"] imageWithTintColor:[UIColor orangeColor]];,得到的結(jié)果為:

嗯…怎么說呢嚼鹉,雖然tintColor的顏色是變了贩汉,但是總覺得怪怪的。仔細(xì)對(duì)比一下就會(huì)發(fā)現(xiàn)锚赤,原來灰色圖里星星和周圍的灰度漸變到了橙色的圖里好像都消失了:星星整個(gè)變成了橙色,周圍的一圈漂亮的光暈也沒有了褐鸥,這是神馬情況啊…這種圖能交差的話那算見鬼了线脚,會(huì)被設(shè)計(jì)和產(chǎn)品打死的吧。對(duì)于無漸變的純色圖的圖來說直接用上面的方法是沒問題的叫榕,但是現(xiàn)在除了Metro的大色塊以外哪里無灰度漸變的設(shè)計(jì)啊…檢查一下使用的blend浑侥,R

= D * Sa,恍然大悟晰绎,我們雖然保留了原色的透明度寓落,但是卻把它的所有的灰度信息弄丟了。怎么辦荞下?繼續(xù)刨CGBlendMode的文檔吧伶选,那么多blend模式應(yīng)該總有我們需要的史飞。功夫不負(fù)有心人,kCGBlendModeOverlay一副嗷嗷待選的樣子:

[objc]view plaincopy

kCGBlendModeOverlay

Either?multiplies?or?screens?the?source?image?samples?with?the?background?image?samples,?depending?on?the?background?color.?The?result?is?to?overlay?the?existing?image?sampleswhilepreserving?the?highlights?and?shadows?of?the?background.?The?background?color?mixes?with?the?source?image?to?reflect?the?lightness?or?darkness?of?the?background.

Available?in?iOS2.0and?later.

Declared?in?CGContext.h.

kCGBlendModeOverlay可以保持背景色的明暗仰税,也就是灰度信息构资,聽起來正是我們需要的。加入到聲明中陨簇,并且添加相應(yīng)的實(shí)現(xiàn)(

順便重構(gòu)一下原來的代碼 :) ):

[objc]view plaincopy

//??UIImage+Tint.h

#import?

@interfaceUIImage?(Tint)

-?(UIImage*)imageWithTintColor:(UIColor*)tintColor;

-?(UIImage*)imageWithGradientTintColor:(UIColor*)tintColor;

@end

[objc]view plaincopy

//??UIImage+Tint.m

#import?"UIImage+Tint.h"

@implementationUIImage?(Tint)

-?(UIImage*)imageWithTintColor:(UIColor*)tintColor

{

return[selfimageWithTintColor:tintColorblendMode:kCGBlendModeDestinationIn];

}

-?(UIImage*)imageWithGradientTintColor:(UIColor*)tintColor

{

return[selfimageWithTintColor:tintColorblendMode:kCGBlendModeOverlay];

}

-?(UIImage*)imageWithTintColor:(UIColor*)tintColorblendMode:(CGBlendMode)blendMode

{

//We?want?to?keep?alpha,?set?opaque?to?NO;?Use?0.0f?for?scale?to?use?the?scale?factor?of?the?device’s?main?screen.

UIGraphicsBeginImageContextWithOptions(self.size,NO,0.0f);

[tintColorsetFill];

CGRect?bounds?=?CGRectMake(0,0,self.size.width,self.size.height);

UIRectFill(bounds);

//Draw?the?tinted?image?in?context

[selfdrawInRect:boundsblendMode:blendModealpha:1.0f];

UIImage*tintedImage?=?UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

returntintedImage;

}

@end

完成吐绵,測試之…好吧,好尷尬河绽,雖然顏色和周圍的光這次對(duì)了己单,但是透明度又沒了啊魂淡..一點(diǎn)不奇怪啊,因?yàn)閗CGBlendModeOverlay本來就沒承諾給你保留原圖的透明度的說耙饰。

那么..既然我們用kCGBlendModeOverlay能保留灰度信息荷鼠,用kCGBlendModeDestinationIn能保留透明度信息,那就兩個(gè)blendMode都用不就完事兒了么~嘗試之榔幸,如果在blend繪圖時(shí)不是kCGBlendModeDestinationIn模式的話允乐,則再用kCGBlendModeDestinationIn畫一次:

[objc]view plaincopy

//??UIImage+Tint.m

#import?"UIImage+Tint.h"

@implementationUIImage?(Tint)

-?(UIImage*)imageWithTintColor:(UIColor*)tintColor

{

return[selfimageWithTintColor:tintColorblendMode:kCGBlendModeDestinationIn];

}

-?(UIImage*)imageWithGradientTintColor:(UIColor*)tintColor

{

return[selfimageWithTintColor:tintColorblendMode:kCGBlendModeOverlay];

}

-?(UIImage*)imageWithTintColor:(UIColor*)tintColorblendMode:(CGBlendMode)blendMode

{

//We?want?to?keep?alpha,?set?opaque?to?NO;?Use?0.0f?for?scale?to?use?the?scale?factor?of?the?device’s?main?screen.

UIGraphicsBeginImageContextWithOptions(self.size,NO,0.0f);

[tintColorsetFill];

CGRect?bounds?=?CGRectMake(0,0,self.size.width,self.size.height);

UIRectFill(bounds);

//Draw?the?tinted?image?in?context

[selfdrawInRect:boundsblendMode:blendModealpha:1.0f];

if(blendMode?!=?kCGBlendModeDestinationIn)?{

[selfdrawInRect:boundsblendMode:kCGBlendModeDestinationInalpha:1.0f];

}

UIImage*tintedImage?=?UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

returntintedImage;

}

@end

結(jié)果如下:

已經(jīng)很完美了,這樣的話只要在代碼里設(shè)定一下顏色削咆,我們就能夠很輕易地使用同樣一套UI牍疏,將其blend為需要的顏色,來實(shí)現(xiàn)素材的重用了拨齐。唯一需要注意的是鳞陨,因?yàn)槊看问褂肬IImage+Tint的方法繪圖時(shí),都使用了CG的繪制方法瞻惋,這就意味著每次調(diào)用都會(huì)是用到CPU的Offscreen drawing厦滤,大量使用的話可能導(dǎo)致性能的問題(主要對(duì)于iPhone 3GS或之前的設(shè)備,可能同時(shí)處理大量這樣的繪制調(diào)用的能力會(huì)有不足)歼狼。關(guān)于CA和CG的性能的問題掏导,打算在之后用一篇文章來介紹一下。對(duì)于這里的UIImage+Tint的實(shí)現(xiàn)羽峰,可以寫一套緩存的機(jī)制趟咆,來確保大量重復(fù)的元素只在load的時(shí)候blend一次,之后將其緩存在內(nèi)存中以快速讀取梅屉。當(dāng)然這是一個(gè)權(quán)衡的問題值纱,在時(shí)間和空間中做出正確的平衡和選擇,也正是程序設(shè)計(jì)的樂趣所在坯汤。

這篇文章中作為示例的工程和UIImage+Tint可以在Github上找到虐唠,您可以隨意玩弄..我相信也會(huì)是個(gè)來研究每種blend的特性的好機(jī)會(huì)~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市惰聂,隨后出現(xiàn)的幾起案子疆偿,更是在濱河造成了極大的恐慌咱筛,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翁脆,死亡現(xiàn)場離奇詭異眷蚓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)反番,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門沙热,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人罢缸,你說我怎么就攤上這事篙贸。” “怎么了枫疆?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵爵川,是天一觀的道長。 經(jīng)常有香客問我息楔,道長寝贡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任值依,我火速辦了婚禮圃泡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘愿险。我一直安慰自己颇蜡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布辆亏。 她就那樣靜靜地躺著风秤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扮叨。 梳的紋絲不亂的頭發(fā)上缤弦,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音甫匹,去河邊找鬼甸鸟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛兵迅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播薪贫,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼恍箭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞧省?” 一聲冷哼從身側(cè)響起扯夭,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤鳍贾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后交洗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骑科,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年构拳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咆爽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡置森,死狀恐怖斗埂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凫海,我是刑警寧澤呛凶,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站行贪,受9級(jí)特大地震影響漾稀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜建瘫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一崭捍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暖混,春花似錦缕贡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至贮配,卻和暖如春谍倦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泪勒。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工昼蛀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人圆存。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓叼旋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沦辙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夫植,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容