详解 UIKit:显示图像数据的高级接口 UIImage

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对象来说,它的数据源主要有以下几种:
文件:我们可以使用init(contentsOfFile:)方法来从指定文件中创建对象。

纯图片数据(NSData):如果在内存中有图片的原始数据(表示为NSData对象),则可以使用init(data:)来创建。需要注意的是这个方法会对象图片数据做缓存。

CGImage对象:如果我们有一个CGImage对象,则可以使用init(CGImage:)或init(CGImage:scale:orientation:)创建UIImage对象。

CIImage对象:如果我们有一个CIImage对象,则可以使用init(CIImage:)或init(CIImage:scale:orientation:)创建UIImage对象。

需要注意的是,如果是从文件或者纯图片数据中创建UIImage对象,则要求对应的图片格式是系统支持的图片类型。
对于Objective-C来说,UIImage对象也提供了这些初始化方法对应的便捷类方法来创建对象。

内存管理
在实际的应用中,特别是图片类应用中,我们可能需要使用大量的图片。我们都知道,图片通常都是非常占内存的。如果同一时间加载大量的图片,就可能占用大量的系统内存。
为此,Apple采用了一种比较巧妙的策略。在低内存的情况下,系统会强制清除UIImage对象所指向的图片数据,以释放部分内存。注意,这种清除行为影响到的只是图片数据,而不会影响到UIImage对象本身。当我们需要绘制那些图片数据已经被清除的UIImage对象时,对象会自动从源文件中重新加载数据。当然,这是以时间换空间的一种策略,会导致一定的性能损耗。
说到这里,我们不得不提一下init(named:)方法了。可以说我们平时创建UIImage对象用得最多的应该就是这个方法。这个方法主要是使用bundle中的文件创建图片的快捷方式。关于这个方法,有几点需要注意:
缓存:这个方法会首先去系统缓存中查找是否有图片名对应的图片。如果有就返回缓存中的图片;如果没有,则该方法从磁盘或者asset catalog中加载图片并返回,同时将图片缓存到系统中。缓存的图片只有在收到内存警告时才会释放。因此,如果图片的使用频率比较低,则可以考虑使用imageWithContentsOfFile:方法来加载图片,这样可以减少内存资源的消耗。当然,这需要权衡考虑,毕竟读写磁盘也是有性能消耗的,而且现在的高端机内存已经不小了。

多分辨率图片处理:在iOS 4.0后,该方法会根据屏幕的分辨率来查找对应尺寸的图片。即我们使用时,只需要写图片名,而不需要指定是1x, 2x还是3x图,该方法会自己判断。

png图片后缀:在iOS 4.0以后,如果图片是png格式的,则图片文件名不需要附带扩展名。

线程安全性:该方法在iOS 9.0之前并不是线程安全的,在二级线程中调用可能会导致崩溃。在iOS 9.0之后,Apple作了优化处理,将其改为线程安全的方法。为了避免不必要的麻烦,尽量在主线程中调用这个方法。

图片拉伸
当我们的图片比所要填充的区域小时,会导致图片变形。如以下图片,原始大小为10030,将其放到一个30050的UIImageView中时,整个图片被拉伸。
原始图片

1448249180736693.png

拉伸后的图片
1448249186297763.png

这时我们就需要做特殊的处理。
Android的同学应该都知道.9图,这种图片可以只拉伸中间的部分,而保持四个角不变形。在iOS中也支持这种操作。在早期的iOS版本中,UIImage提供了如下方法来执行此操作:
func stretchableImageWithLeftCapWidth(_ leftCapWidth: Int, topCapHeight topCapHeight: Int) -> UIImage
这个方法通过leftCapWidth和topCapHeight两个参数来定义四个角的大小。不过这个方法在iOS 5中就被Deprecated了,对应的两个属性leftCapWidth和topCapHeight也是相同的命运。所以现在不建议使用它们。另外,对于如何解释leftCapWidth和topCapHeight,大家可以参考一下@M了个JiOS图片拉伸技巧
在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)

其得到的结果如下所示:


1448249217185232.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”这样的一系列的文件。
这里有两个问题需要注意:
文件的序号必须是从0开始的连续数字,如果不从0开始,则在Playground中是会报错的。而如果中间序号有断,而中断后的图片是不会被加载的。

所有文件的大小和缩放因子应该是相同的,否则显示时会有不可预期的结果,这种结果主要表现为播放的顺序可能是杂乱的。

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

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对象使用的图片大小尽量小于10241024。因为这么大的图片消耗的内存过大,在将其作为OpenGL中的贴图或者是绘制到view/layer中时,可以会出现问题。如果仅仅是代码层面的操作的话,则没有这个限制。比如,将一个大于10241024的图片绘制到位图图形上下文中以重新设定其大小。事实上,我们需要通过这种操作来改变图片大小,以将其绘制到视图中。
支持的图片格式
UIImage支持的图片格式在UIImage Class Reference中列出来了,大家可以直接参考。
需要注意的一点是RGB-565格式的BMP文件在加载时会被转换成ARGB-1555格式。
示例代码
本文的示例代码已上传到github,可点击这里查看。
参考
UIImage Class Reference

iOS图片拉伸技巧

iOS 处理图片的一些小 Tip

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,107评论 29 470
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,945评论 4 60
  • 本文转载自:http://www.cocoachina.com/ios/20150106/10840.html 为...
    idiot_lin阅读 617评论 0 1
  • 最近在刷一部新小说。 接近700章节的长度,主人公从小白开始成长,一步步打怪升级换地图。等待的时候刷刷,地铁的时候...
    西苏Sisu阅读 261评论 0 0