本文idea参考了objc期刊的部分文章,本文demo地址可以在Light & shallow中看到。
一、图片种类
矢量图
iOS支持两种格式的矢量图:PDF和SVG。我们先来理解一下矢量图,它其实是一组绘图指令,并且独立于尺寸存在。最大的优点就是无论放大缩小还是旋转,都不会失真。比如在绘制图形时,如果你想扩大一个圆形,只需在绘制前扩大它的半径就可以了。位图则没这么容易,最起码,如果扩大的比例不是二的倍数,就会涉及到重绘图像。由于我们不知道这图像是一个圆形,所以无法确保弧线的准确描绘,效果看起来肯定不如按比例绘制的线条那样好。也因此,在像素密度不同的设备中,矢量图像作为图形资源会非常有用。位图的话,同样的图标,在视网膜屏幕之前的 iPhone 上看起来并没有问题,在拉伸两倍后的视网膜屏幕上看起来就会发虚。就好像仅适配了 iPhone 的 App 运行在 iPad 的 2x 模式下就不再那么清晰了。
Xcode虽然支持PDF格式的矢量图,然而支持的却并不是那么完善,有点蒙蔽的意识。对于一个PDF图片,Xcode实际上在编译阶段对应的生成了1x,2x,3x的png图(这个1x图就是PDF图片本身),因此让开发者看起来Xcode支持了PDF的矢量图。
位图
大部分图像都是以位图方式处理的,如我们常见的图片格式GIF,PNG,JPEG等。GIF
和 PNG
格式对于具有大面积相同颜色的图像是最好的选择,因为它们使用的压缩算法可以减少存储需求。这种压缩是无损的,这意味着图像质量不会被压缩影响。
一个有损压缩图像格式的例子是 JPEG
。创建 JPEG 图像时,通常会指定一个与图像质量相关的压缩比值参数,压缩程度过高会导致图像质量变差。JPEG 不适用于对比鲜明的图像 (如线条图),其压缩方式对类似区域的图像质量损害会相对严重。如果某张截图中包含了文本,且保存为 JPEG 格式,就可以清楚地看到:生成的图像中字符周围会出现杂散的像素点。在大部分照片中不存在这个问题,所以照片主要使用 JPEG 格式。
二、图片属性(图片的元数据信息)
用于存储图像信息的标准格式是 Exif(可交换图像文件格式)。在照片中,通常会捕获照相的日期和时间,图片方向,如果设备支持,还包括 GPS 坐标。这也就是当有人在微信朋友圈发了一张在自己家鱼缸的照片,却配文说在马尔代夫潜泳时,会被人识破的原因。
在 iOS 中,可以使用 CGImageSourceCopyPropertiesAtIndex
方法访问 Exif 信息。这个方法会返回一个包含所有相关信息的字典。
//1.获取图片文件
NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"YourPic" withExtension:@""];
//2.创建CGImageSourceRef
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)fileUrl, NULL);
//3.利用imageSource获取ExifData
CFDictionaryRef imageInfo = CGImageSourceCopyPropertiesAtIndex(imageSource, 0,NULL);
NSDictionary *exifDic = (__bridge NSDictionary *)CFDictionaryGetValue(imageInfo, kCGImagePropertyExifDictionary) ;
三、图片处理框架FrameWork
在 iOS 中有几个类是用来处理位图数据的:UIImage (UIKit),CGImage (Core Graphics) 和 CIImage (Core Image)。在创建以上类的实例之前,由 NSData 持有实际的数据。获得一个 UIImage 最简单的方法是使用 imageWithContentsOfFile: 方法,或者根据其来源,也可以使用 imageWithCGImage:,imageWithCIImage: 或者 imageWithData:。有多个不同却相似的类看起来有点多余,部分原因是因为它们来自不同的框架,对于图像储存的优化方式也各有侧重,通常情况下,不同类型之间是可以轻松转换的。
在iOS8之前,开发者只能使用AssetsLibrary框架访问用户相册库,而近几年,由于照片应用增加了很多新的特性,AssetsLibrary框架已经无法满足需求,于是PhotoKit框架应运而生。PhotoKit与设备照片库是无缝对接的,就是说PhotoKit 定义了与系统的 Photos 应用内展现给用户的模型对象相一致的实体图表。
PhotoKit对象模型
所有的 PhotoKit 对象都是继承自 PHObject 抽象基类,其公共接口只提供了一个 localIdentifier 属性。
PHOTOS_CLASS_AVAILABLE_IOS_TVOS(8_0, 10_0) @interface PHObject : NSObject <NSCopying>
// Returns an identifier which persistently identifies the object on a given device
@property (nonatomic, copy, readonly) NSString *localIdentifier;
@end
我们来看几个经常用到的类:
PHAsset
表示用户照片库中一个单独的资源,用以提供资源的元数据。
成组的资源叫做资源集合,用 PHAssetCollection
类表示。一个单独的资源集合可以是照片库中的一个相册或者一个时刻,或者是一个特殊的“智能相册”。这种智能相册包括所有的视频集合,最近添加的项目,用户收藏,所有连拍照片等等,PHAssetCollection
是 PHCollection
的子类。
PHCollectionList
表示一组的 PHCollections
。因为它本身就是 PHCollection
,所以集合列表可以包含其他集合列表,它们允许复杂的集合继承。实际上,我们可以在照片应用的时刻栏目中看到它:照片 --- 时刻 --- 精选 --- 年度,就是一个例子。
获取 (Fetch) 照片实体
我们知道通过AssetLibrary框架获取资源的方式是通过枚举用户资源库,然后进行遍历匹配来得到,这不得不说非常低效。
而PhotoKit 实体的实例是通过获取得到的。
获取请求
获取操作是由上面描述的实体的类方法实现的。要使用哪个类/方法,取决于问题所在范围和你展示与遍历照片库的方式。所有获取方法的命名都是相似的:class func fetchXXX(..., options: PHFetchOptions) -> PHFetchResult
。options
参数给了我们一个对结果进行过滤和排序的途径,这和 NSFetchRequest
的 predicate
与 sortDescriptors
参数类似。
获取结果
你可能已经注意到了这些获取操作不是异步的。它们返回了一个 PHFetchResult
对象,可以用类似 NSArray
的接口来访问结果内的集合。它会按需动态加载内容并且缓存最近请求的内容。这个行为和设置了 batchSize
属性的 NSFetchRequest
返回的结果数组相似。对于 PHFetchResult
来说,没有办法用参数来指定这个行为,但是官网文档保证 “即使在处理大量的返回结果时,依然能够有最好的表现”。
就算满足请求的照片库内容发生了改变,获取方法所返回的 PHFetchResult
对象是不会自动更新。
这一部分的代码你可以在Light & shallow中看到。
网友评论