源码地址
github
此例子是在写相册选择器时获取图片资源时所用,
主要解决了获取图片的缓存问题和尺寸问题,简化了逻辑和结构
使代码十分清晰明了且易于理解
此处只显示核心代码,详细见源码
show me code
public typealias ImageOfIndex = (Int)-> UIImage
private let imageManager = PHCachingImageManager()
/// get thumbnail creater
///
/// - Parameters:
/// - assets: PHAsset list
/// - size: target size
/// - Returns: thumbnail creater
public func getThumbnailImage(_ assets: [PHAsset], targetSize size: CGSize) -> ImageOfIndex {
let options = PHImageRequestOptions()
options.deliveryMode = .opportunistic
options.isSynchronous = false
return getImageByConfig(options, assets, size)
}
/// get highImage creater
///
/// - Parameters:
/// - assets: PHAsset list
/// - size: target size
/// - Returns: highImage creater
public func getHighImage(_ assets: [PHAsset], targetSize size: CGSize) -> ImageOfIndex {
let options = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
options.isSynchronous = false
return getImageByConfig(options, assets, size)
}
/// get image creater by config
///
/// - Parameters:
/// - option: PHImageRequestOptions
/// - assets: PHAsset list
/// - size: target size
/// - Returns: image creater
private func getImageByConfig(_ option: PHImageRequestOptions, _ assets: [PHAsset], _ size: CGSize) -> ImageOfIndex {
imageManager.startCachingImages(for: assets, targetSize: size, contentMode: .aspectFill, options: option)
//image creater
func getImage(_ index: Int) -> UIImage {
let option = PHImageRequestOptions()
option.isSynchronous = true
var image = UIImage()
imageManager.requestImage(for: assets[index], targetSize: size, contentMode: .aspectFill, options: option, resultHandler:{(result, info)->Void in
image = result!
})
return image
}
return getImage;
}
剖析
ios8.0可以使用PHAsset获取相册、图片等资源,由PHImageManager根据PHAsset对象获取实际图片,
那么问题来了:
我是用collectionView展示的,那么数据源我保存什么,图片数组?PHAsset数组?
答:图片资源较大,占用内存较多,且分为缩略图和高清图,直接保存会占用极多内存,尤其在图片资源较多的时候;那么就可以保存PHAsset,这个只是图片资源的描述,并不是图片本身,占用内存极小(相对图片来说),且可以根据该对象动态选择获取什么尺寸的图片
如何缓存图片资源?
答:翻找Photos的API,找到了PHCachingImageManager ,可以批量缓存,缓存后再获取速度极快,且占用内存小。PS:经测试:在手机大约1000张时,缓存原图 只占用20M,缩略图只有13M
如何方便快捷的获取图片,因为在collectionView展示,我想只根据indexPath.row序号就能获取缓存的图片,而不是每次根据序号取出asset对象,再传入其余参数配置(要和缓存时参数配置一样)才能取出图片?
答:想到了Swift文档中关于 ''嵌套函数''的介绍,在swift中函数作为一等公民,可以当做一般对象使用,可以在内部函数内部声明新的函数,并把此函数当做结果返回出去,同时,内部函数拥有访问外部函数变量的权限,且拥有类似懒加载的特性(内部函数并不是立马执行,而是每调用一次才执行一次)
详解getImageByConfig()函数
无论获取缩略图还是高清图,我最都是调用内部的这个方法,看看这个方法都做了什么。
- 根据参数配置,做了图片缓存
- 返回了一个图片生成器
这个图片生成器又做了什么?
由于getImage()是内部函数,所以它可以拿到外部函数的所有参数,包括asset集合和其余参数配置,然后该函数根据指定的索引序号获取图片。
使用
internal var getThumbnail: ImageOfIndex?
internal var getHighImage: ImageOfIndex?
private func getAssetsImage() {
DispatchQueue.global().async {
self.getThumbnail = self.asset.getThumbnailImage(self.assetsList!, targetSize: self.flowLayout.itemSize)
self.getHighImage = self.asset.getHighImage(self.assetsList!, targetSize: UIScreen.main.bounds.size)
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: YHPhotoThumbnailCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! YHPhotoThumbnailCell;
cell.setImage(getThumbnail!(indexPath.row))
return cell
}
在进入控制器时获取了asset集合,并对图片进行了缓存,并将函数返回的图片生成器的函数保存当前类的全局变量中,self.getThumbnail,self.getHighImage(这两个变量都是函数类型的)
在需要使用的时候,给该变量传入所需的索引号,就能方便的获取事先缓存好的图片。是不是很简洁!
个人感觉:
内部函数类似于block,可以访问外部函数的变量
block是匿名函数(没有方法名),若存在于某个函数内也算是内部函数吧
自己都被swift这种简洁优雅的实现震撼了,将函数的依赖降到了最低。
看~灰机~灰机灰过来了~灰机又灰过去了~