詳解 UIKit:顯示圖像數(shù)據(jù)的高級(jí)接口 UIImage

UIImage對(duì)象是iOS中用來(lái)顯示圖像數(shù)據(jù)的高級(jí)接口。我們可以從文件凫海,NSData呛凶,Quartz圖片對(duì)象中創(chuàng)建UIImage對(duì)象⌒刑埃可以說(shuō)這個(gè)類(lèi)是我們接觸頻率非常高的一個(gè)類(lèi)漾稀。
UIImage的不可變性
UIImage對(duì)象是不可變的,所以一旦創(chuàng)建后建瘫,我們就不能再改變它的屬性崭捍。這也就意味著,我們只能在初始化方法中提供屬性值或依賴(lài)于圖片自身的屬性值啰脚。同樣殷蛇,由于其不可變,所以在任何線程中都可以安全地使用它。
如果我們想修改UIImage對(duì)象的一些屬性粒梦,則可以使用便捷方法和自定義的參數(shù)值來(lái)創(chuàng)建圖像的一份拷貝亮航。
另外,由于UIImage對(duì)象是不可變的匀们,所以它沒(méi)有提供訪問(wèn)底層圖片數(shù)據(jù)的方法缴淋。不過(guò)我們可以使用UIImagePNGRepresentation或UIImageJPEGRepresentation方法來(lái)獲取包含PNG或JPG格式的數(shù)據(jù)的NSData對(duì)象。如下代碼所示:
let image = UIImage(named: "swift");let imageData:NSData? = UIImageJPEGRepresentation(image!, 1.0)

創(chuàng)建UIImage對(duì)象
對(duì)于一個(gè)UIImage對(duì)象來(lái)說(shuō)泄朴,它的數(shù)據(jù)源主要有以下幾種:
文件:我們可以使用init(contentsOfFile:)方法來(lái)從指定文件中創(chuàng)建對(duì)象重抖。

純圖片數(shù)據(jù)(NSData):如果在內(nèi)存中有圖片的原始數(shù)據(jù)(表示為NSData對(duì)象),則可以使用init(data:)來(lái)創(chuàng)建祖灰。需要注意的是這個(gè)方法會(huì)對(duì)象圖片數(shù)據(jù)做緩存钟沛。

CGImage對(duì)象:如果我們有一個(gè)CGImage對(duì)象,則可以使用init(CGImage:)或init(CGImage:scale:orientation:)創(chuàng)建UIImage對(duì)象夫植。

CIImage對(duì)象:如果我們有一個(gè)CIImage對(duì)象讹剔,則可以使用init(CIImage:)或init(CIImage:scale:orientation:)創(chuàng)建UIImage對(duì)象油讯。

需要注意的是详民,如果是從文件或者純圖片數(shù)據(jù)中創(chuàng)建UIImage對(duì)象,則要求對(duì)應(yīng)的圖片格式是系統(tǒng)支持的圖片類(lèi)型陌兑。
對(duì)于Objective-C來(lái)說(shuō)沈跨,UIImage對(duì)象也提供了這些初始化方法對(duì)應(yīng)的便捷類(lèi)方法來(lái)創(chuàng)建對(duì)象。

內(nèi)存管理
在實(shí)際的應(yīng)用中兔综,特別是圖片類(lèi)應(yīng)用中饿凛,我們可能需要使用大量的圖片。我們都知道软驰,圖片通常都是非常占內(nèi)存的涧窒。如果同一時(shí)間加載大量的圖片,就可能占用大量的系統(tǒng)內(nèi)存锭亏。
為此纠吴,Apple采用了一種比較巧妙的策略。在低內(nèi)存的情況下慧瘤,系統(tǒng)會(huì)強(qiáng)制清除UIImage對(duì)象所指向的圖片數(shù)據(jù)戴已,以釋放部分內(nèi)存。注意锅减,這種清除行為影響到的只是圖片數(shù)據(jù)糖儡,而不會(huì)影響到UIImage對(duì)象本身。當(dāng)我們需要繪制那些圖片數(shù)據(jù)已經(jīng)被清除的UIImage對(duì)象時(shí)怔匣,對(duì)象會(huì)自動(dòng)從源文件中重新加載數(shù)據(jù)握联。當(dāng)然,這是以時(shí)間換空間的一種策略,會(huì)導(dǎo)致一定的性能損耗金闽。
說(shuō)到這里永部,我們不得不提一下init(named:)方法了∧欧可以說(shuō)我們平時(shí)創(chuàng)建UIImage對(duì)象用得最多的應(yīng)該就是這個(gè)方法苔埋。這個(gè)方法主要是使用bundle中的文件創(chuàng)建圖片的快捷方式。關(guān)于這個(gè)方法蜒犯,有幾點(diǎn)需要注意:
緩存:這個(gè)方法會(huì)首先去系統(tǒng)緩存中查找是否有圖片名對(duì)應(yīng)的圖片组橄。如果有就返回緩存中的圖片;如果沒(méi)有罚随,則該方法從磁盤(pán)或者asset catalog中加載圖片并返回玉工,同時(shí)將圖片緩存到系統(tǒng)中。緩存的圖片只有在收到內(nèi)存警告時(shí)才會(huì)釋放淘菩。因此遵班,如果圖片的使用頻率比較低,則可以考慮使用imageWithContentsOfFile:方法來(lái)加載圖片潮改,這樣可以減少內(nèi)存資源的消耗狭郑。當(dāng)然,這需要權(quán)衡考慮汇在,畢竟讀寫(xiě)磁盤(pán)也是有性能消耗的翰萨,而且現(xiàn)在的高端機(jī)內(nèi)存已經(jīng)不小了。

多分辨率圖片處理:在iOS 4.0后糕殉,該方法會(huì)根據(jù)屏幕的分辨率來(lái)查找對(duì)應(yīng)尺寸的圖片亩鬼。即我們使用時(shí),只需要寫(xiě)圖片名阿蝶,而不需要指定是1x, 2x還是3x圖雳锋,該方法會(huì)自己判斷。

png圖片后綴:在iOS 4.0以后羡洁,如果圖片是png格式的玷过,則圖片文件名不需要附帶擴(kuò)展名。

線程安全性:該方法在iOS 9.0之前并不是線程安全的焚廊,在二級(jí)線程中調(diào)用可能會(huì)導(dǎo)致崩潰冶匹。在iOS 9.0之后,Apple作了優(yōu)化處理咆瘟,將其改為線程安全的方法嚼隘。為了避免不必要的麻煩,盡量在主線程中調(diào)用這個(gè)方法袒餐。

圖片拉伸
當(dāng)我們的圖片比所要填充的區(qū)域小時(shí)飞蛹,會(huì)導(dǎo)致圖片變形谤狡。如以下圖片,原始大小為10030卧檐,將其放到一個(gè)30050的UIImageView中時(shí)墓懂,整個(gè)圖片被拉伸。
原始圖片

1448249180736693.png

拉伸后的圖片
1448249186297763.png

這時(shí)我們就需要做特殊的處理霉囚。
Android的同學(xué)應(yīng)該都知道.9圖捕仔,這種圖片可以只拉伸中間的部分,而保持四個(gè)角不變形盈罐。在iOS中也支持這種操作榜跌。在早期的iOS版本中,UIImage提供了如下方法來(lái)執(zhí)行此操作:
func stretchableImageWithLeftCapWidth(_ leftCapWidth: Int, topCapHeight topCapHeight: Int) -> UIImage
這個(gè)方法通過(guò)leftCapWidth和topCapHeight兩個(gè)參數(shù)來(lái)定義四個(gè)角的大小盅粪。不過(guò)這個(gè)方法在iOS 5中就被Deprecated了钓葫,對(duì)應(yīng)的兩個(gè)屬性leftCapWidth和topCapHeight也是相同的命運(yùn)。所以現(xiàn)在不建議使用它們票顾。另外础浮,對(duì)于如何解釋leftCapWidth和topCapHeight,大家可以參考一下@M了個(gè)JiOS圖片拉伸技巧奠骄。
在iOS 5中豆同,我們可以使用以下方法來(lái)執(zhí)行相同的操作:
func resizableImageWithCapInsets(_ capInsets: UIEdgeInsets) -> UIImage
這個(gè)方法通過(guò)一個(gè)UIEdgeInsets來(lái)指定上下左右不變形的寬度或高度。它會(huì)返回一個(gè)新的圖像戚揭。而如果圖像被拉伸诱告,則會(huì)以平鋪的方式來(lái)處理中間的拉伸區(qū)域撵枢。
我們對(duì)上面的圖片做如下處理:

let resizedButtonImageView = UIImageView(image: normalButtonImage?.resizableImageWithCapInsets(UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)))
resizedButtonImageView.frame = CGRectMake(0, 60, 300, 50)

其得到的結(jié)果如下所示:


1448249217185232.png

在iOS 6民晒,Apple又為我們提供了一個(gè)新的方法,相較于上面這個(gè)方法锄禽,只是多一個(gè)resizingMode參數(shù)潜必,允許我們指定拉伸模式。

func resizableImageWithCapInsets(_ capInsets: UIEdgeInsets, resizingMode resizingMode: UIImageResizingMode) -> UIImage

這個(gè)方法的拉伸模式分兩種:平鋪(Tile)和拉伸(Stretch)沃但。如果是平鋪模式磁滚,則跟前一個(gè)方法是一樣的效果。
動(dòng)效圖片對(duì)象
如果我們有一組大小和縮放因子相同的圖片宵晚,就可以將這些圖片加載到同一個(gè)UIImage對(duì)象中垂攘,形成一個(gè)動(dòng)態(tài)的UIImage對(duì)象。為此淤刃,UIImage提供了以下方法:

class func animatedImageNamed(_ name: String, duration duration: NSTimeInterval) -> UIImage?

這個(gè)方法會(huì)加載以name為基準(zhǔn)文件名的一系列文件晒他。如,假設(shè)我們的name參數(shù)值為”swift”逸贾,則這個(gè)方法會(huì)加載諸如”swift0”, “swift1”,…, “swift1024”這樣的一系列的文件陨仅。
這里有兩個(gè)問(wèn)題需要注意:
文件的序號(hào)必須是從0開(kāi)始的連續(xù)數(shù)字津滞,如果不從0開(kāi)始,則在Playground中是會(huì)報(bào)錯(cuò)的灼伤。而如果中間序號(hào)有斷触徐,而中斷后的圖片是不會(huì)被加載的。

所有文件的大小和縮放因子應(yīng)該是相同的狐赡,否則顯示時(shí)會(huì)有不可預(yù)期的結(jié)果撞鹉,這種結(jié)果主要表現(xiàn)為播放的順序可能是雜亂的。

如果我們有一組基準(zhǔn)文件名不同的文件颖侄,但其大小和縮放因子相同孔祸,則可能使用以下方法:

class func animatedImageWithImages(_ images: [UIImage], duration duration: NSTimeInterval) -> UIImage?

傳入一個(gè)UIImage數(shù)組來(lái)拼裝一個(gè)動(dòng)效UIImage對(duì)象。
另外发皿,UIImage也提供了resizable版本的動(dòng)效方法崔慧,如下所示:

class func animatedResizableImageNamed(_ name: String, capInsets capInsets: UIEdgeInsets, duration duration: NSTimeInterval) -> UIImage?
class func animatedResizableImageNamed(_ name: String, capInsets capInsets: UIEdgeInsets, resizingMode resizingMode: UIImageResizingMode, duration duration: NSTimeInterval) -> UIImage?

第一個(gè)方法的UIImageResizingMode默認(rèn)是UIImageResizingModeTile,所以如果想對(duì)圖片做拉伸處理穴墅,可以使用第二個(gè)的方法惶室,并傳入U(xiǎn)IImageResizingModeStretch。
圖片大小的限制
UIImage對(duì)象使用的圖片大小盡量小于10241024玄货。因?yàn)檫@么大的圖片消耗的內(nèi)存過(guò)大皇钞,在將其作為OpenGL中的貼圖或者是繪制到view/layer中時(shí),可以會(huì)出現(xiàn)問(wèn)題松捉。如果僅僅是代碼層面的操作的話夹界,則沒(méi)有這個(gè)限制。比如隘世,將一個(gè)大于10241024的圖片繪制到位圖圖形上下文中以重新設(shè)定其大小可柿。事實(shí)上,我們需要通過(guò)這種操作來(lái)改變圖片大小丙者,以將其繪制到視圖中复斥。
支持的圖片格式
UIImage支持的圖片格式在UIImage Class Reference中列出來(lái)了,大家可以直接參考械媒。
需要注意的一點(diǎn)是RGB-565格式的BMP文件在加載時(shí)會(huì)被轉(zhuǎn)換成ARGB-1555格式目锭。
示例代碼
本文的示例代碼已上傳到github,可點(diǎn)擊這里查看纷捞。
參考
UIImage Class Reference

iOS圖片拉伸技巧

iOS 處理圖片的一些小 Tip

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末痢虹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子主儡,更是在濱河造成了極大的恐慌奖唯,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缀辩,死亡現(xiàn)場(chǎng)離奇詭異臭埋,居然都是意外死亡踪央,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)瓢阴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)畅蹂,“玉大人,你說(shuō)我怎么就攤上這事荣恐∫盒保” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵叠穆,是天一觀的道長(zhǎng)少漆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)硼被,這世上最難降的妖魔是什么示损? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮嚷硫,結(jié)果婚禮上检访,老公的妹妹穿的比我還像新娘。我一直安慰自己仔掸,他們只是感情好脆贵,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著起暮,像睡著了一般卖氨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上负懦,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天筒捺,我揣著相機(jī)與錄音,去河邊找鬼密似。 笑死焙矛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的残腌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贫导,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抛猫!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起孩灯,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤闺金,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后峰档,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體败匹,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寨昙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掀亩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舔哪。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖槽棍,靈堂內(nèi)的尸體忽然破棺而出捉蚤,到底是詐尸還是另有隱情,我是刑警寧澤炼七,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布缆巧,位于F島的核電站,受9級(jí)特大地震影響豌拙,放射性物質(zhì)發(fā)生泄漏陕悬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一按傅、第九天 我趴在偏房一處隱蔽的房頂上張望墩莫。 院中可真熱鬧,春花似錦逞敷、人聲如沸狂秦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)裂问。三九已至,卻和暖如春牛柒,著一層夾襖步出監(jiān)牢的瞬間堪簿,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工皮壁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椭更,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓蛾魄,卻偏偏與公主長(zhǎng)得像虑瀑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子滴须,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,514評(píng)論 25 707
  • *面試心聲:其實(shí)這些題本人都沒(méi)怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,125評(píng)論 29 470
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)舌狗、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,029評(píng)論 4 62
  • 本文轉(zhuǎn)載自:http://www.cocoachina.com/ios/20150106/10840.html 為...
    idiot_lin閱讀 628評(píng)論 0 1
  • 最近在刷一部新小說(shuō)扔水。 接近700章節(jié)的長(zhǎng)度痛侍,主人公從小白開(kāi)始成長(zhǎng),一步步打怪升級(jí)換地圖魔市。等待的時(shí)候刷刷主届,地鐵的時(shí)候...
    西蘇Sisu閱讀 263評(píng)論 0 0