你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 詳解 UIKit:顯示圖像數據的高級接口 UIImage

詳解 UIKit:顯示圖像數據的高級接口 UIImage

編輯:IOS開發基礎

1.jpg

本系列主要基於Apple官方文檔,更多的是對參考文檔重點內容的翻譯與補充。該系列中的每篇文章會持續更新與補充。如有問題,歡迎通過微博告訴我,我將及時進行更正,謝謝!!!

UIImage對象是iOS中用來顯示圖像數據的高級接口。我們可以從文件,NSData,Quartz圖片對象中創建UIImage對象。可以說這個類是我們接觸頻率非常高的一個類。

UIImage的不可變性

UIImage對象是不可變的,所以一旦創建後,我們就不能再改變它的屬性。這也就意味著,我們只能在初始化方法中提供屬性值或依賴於圖片自身的屬性值。同樣,由於其不可變,所以在任何線程中都可以安全地使用它。

如果我們想修改UIImage對象的一些屬性,則可以使用便捷方法和自定義的參數值來創建圖像的一份拷貝。

另外,由於UIImage對象是不可變的,所以它沒有提供訪問底層圖片數據的方法。不過我們可以使用UIImagePNGRepresentation或UIImageJPEGRepresentation方法來獲取包含PNG或JPG格式的數據的NSData對象。如下代碼所示:

let image = UIImage(named: "swift");
let imageData:NSData? = UIImageJPEGRepresentation(image!, 1.0)

創建UIImage對象

對於一個UIImage對象來說,它的數據源主要有以下幾種:

  1. 文件:我們可以使用init(contentsOfFile:)方法來從指定文件中創建對象。

  2. 純圖片數據(NSData):如果在內存中有圖片的原始數據(表示為NSData對象),則可以使用init(data:)來創建。需要注意的是這個方法會對象圖片數據做緩存。

  3. CGImage對象:如果我們有一個CGImage對象,則可以使用init(CGImage:)或init(CGImage:scale:orientation:)創建UIImage對象。

  4. CIImage對象:如果我們有一個CIImage對象,則可以使用init(CIImage:)或init(CIImage:scale:orientation:)創建UIImage對象。

需要注意的是,如果是從文件或者純圖片數據中創建UIImage對象,則要求對應的圖片格式是系統支持的圖片類型。

對於Objective-C來說,UIImage對象也提供了這些初始化方法對應的便捷類方法來創建對象。

內存管理

在實際的應用中,特別是圖片類應用中,我們可能需要使用大量的圖片。我們都知道,圖片通常都是非常占內存的。如果同一時間加載大量的圖片,就可能占用大量的系統內存。

為此,Apple采用了一種比較巧妙的策略。在低內存的情況下,系統會強制清除UIImage對象所指向的圖片數據,以釋放部分內存。注意,這種清除行為影響到的只是圖片數據,而不會影響到UIImage對象本身。當我們需要繪制那些圖片數據已經被清除的UIImage對象時,對象會自動從源文件中重新加載數據。當然,這是以時間換空間的一種策略,會導致一定的性能損耗。

說到這裡,我們不得不提一下init(named:)方法了。可以說我們平時創建UIImage對象用得最多的應該就是這個方法。這個方法主要是使用bundle中的文件創建圖片的快捷方式。關於這個方法,有幾點需要注意:

  1. 緩存:這個方法會首先去系統緩存中查找是否有圖片名對應的圖片。如果有就返回緩存中的圖片;如果沒有,則該方法從磁盤或者asset catalog中加載圖片並返回,同時將圖片緩存到系統中。緩存的圖片只有在收到內存警告時才會釋放。因此,如果圖片的使用頻率比較低,則可以考慮使用imageWithContentsOfFile:方法來加載圖片,這樣可以減少內存資源的消耗。當然,這需要權衡考慮,畢竟讀寫磁盤也是有性能消耗的,而且現在的高端機內存已經不小了。

  2. 多分辨率圖片處理:在iOS 4.0後,該方法會根據屏幕的分辨率來查找對應尺寸的圖片。即我們使用時,只需要寫圖片名,而不需要指定是1x, 2x還是3x圖,該方法會自己判斷。

  3. png圖片後綴:在iOS 4.0以後,如果圖片是png格式的,則圖片文件名不需要附帶擴展名。

  4. 線程安全性:該方法在iOS 9.0之前並不是線程安全的,在二級線程中調用可能會導致崩潰。在iOS 9.0之後,Apple作了優化處理,將其改為線程安全的方法。為了避免不必要的麻煩,盡量在主線程中調用這個方法。

圖片拉伸

當我們的圖片比所要填充的區域小時,會導致圖片變形。如以下圖片,原始大小為100*30,將其放到一個300*50的UIImageView中時,整個圖片被拉伸。

原始圖片

blob.png

拉伸後的圖片

blob.png

這時我們就需要做特殊的處理。

Android的同學應該都知道.9圖,這種圖片可以只拉伸中間的部分,而保持四個角不變形。在iOS中也支持這種操作。在早期的iOS版本中,UIImage提供了如下方法來執行此操作:

func stretchableImageWithLeftCapWidth(_ leftCapWidth: Int, topCapHeight topCapHeight: Int) -> UIImage

這個方法通過leftCapWidth和topCapHeight兩個參數來定義四個角的大小。不過這個方法在iOS 5中就被Deprecated了,對應的兩個屬性leftCapWidth和topCapHeight也是相同的命運。所以現在不建議使用它們。另外,對於如何解釋leftCapWidth和topCapHeight,大家可以參考一下@M了個J的iOS圖片拉伸技巧。

在iOS 5中,我們可以使用以下方法來執行相同的操作:

func resizableImageWithCapInsets(_ capInsets: UIEdgeInsets) -> UIImage

這個方法通過一個UIEdgeInsets來指定上下左右不變形的寬度或高度。它會返回一個新的圖像。而如果圖像被拉伸,則會以平鋪的方式來處理中間的拉伸區域。

我們對上面的圖片做如下處理:

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

其得到的結果如下所示:

blob.png

在iOS 6,Apple又為我們提供了一個新的方法,相較於上面這個方法,只是多一個resizingMode參數,允許我們指定拉伸模式。

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

這個方法的拉伸模式分兩種:平鋪(Tile)和拉伸(Stretch)。如果是平鋪模式,則跟前一個方法是一樣的效果。

動效圖片對象

如果我們有一組大小和縮放因子相同的圖片,就可以將這些圖片加載到同一個UIImage對象中,形成一個動態的UIImage對象。為此,UIImage提供了以下方法:

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

這個方法會加載以name為基准文件名的一系列文件。如,假設我們的name參數值為”swift”,則這個方法會加載諸如”swift0”, “swift1”,…, “swift1024”這樣的一系列的文件。

這裡有兩個問題需要注意:

  1. 文件的序號必須是從0開始的連續數字,如果不從0開始,則在Playground中是會報錯的。而如果中間序號有斷,而中斷後的圖片是不會被加載的。

  2. 所有文件的大小和縮放因子應該是相同的,否則顯示時會有不可預期的結果,這種結果主要表現為播放的順序可能是雜亂的。

如果我們有一組基准文件名不同的文件,但其大小和縮放因子相同,則可能使用以下方法:

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

傳入一個UIImage數組來拼裝一個動效UIImage對象。

另外,UIImage也提供了resizable版本的動效方法,如下所示:

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?

第一個方法的UIImageResizingMode默認是UIImageResizingModeTile,所以如果想對圖片做拉伸處理,可以使用第二個的方法,並傳入UIImageResizingModeStretch。

圖片大小的限制

UIImage對象使用的圖片大小盡量小於1024*1024。因為這麼大的圖片消耗的內存過大,在將其作為OpenGL中的貼圖或者是繪制到view/layer中時,可以會出現問題。如果僅僅是代碼層面的操作的話,則沒有這個限制。比如,將一個大於1024*1024的圖片繪制到位圖圖形上下文中以重新設定其大小。事實上,我們需要通過這種操作來改變圖片大小,以將其繪制到視圖中。

支持的圖片格式

UIImage支持的圖片格式在UIImage Class Reference中列出來了,大家可以直接參考。

需要注意的一點是RGB-565格式的BMP文件在加載時會被轉換成ARGB-1555格式。

示例代碼

本文的示例代碼已上傳到github,可點擊這裡查看。

參考

  • UIImage Class Reference

  • iOS圖片拉伸技巧

  • iOS 處理圖片的一些小 Tip


  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved