iOS 基于PhotoKit 获取系统所有相册 以及所有照片 包括iCloud的处理 细节详解及实战代码
前言
最近在做一个相册的项目,一开始觉得项目没什么难度,可是真正上手做了之后,发现难度不小,苹果新推出Photokit之后,获取相册中图片对象是快的,可是在获取到具体图片个人实战比较慢,同时还有icloud的问题,那么本片博客就带你一起解决这些问题。
第一步 获取系统所有相册
import Photos private var allAsset:[PHAsset] = [] //相册中所有的照片 /* 获取相机胶卷数据 */ let cameraRoll = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil) cameraRoll.enumerateObjects(options: .concurrent) { (collection, index, stop) in let assetResult = PHAsset.fetchAssets(in: collection, options: options) for i in 0..<assetResult.count{ self.allAsset.append(assetResult[i]) } } /* 获取用户自定义相册 */ let userAlbums = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil) userAlbums.enumerateObjects(options: .concurrent) { (collection, index, stop) in let assetResult = PHAsset.fetchAssets(in: collection, options: options) if collection.localizedTitle! != "最近删除"{ for i in 0..<assetResult.count{ self.allAsset.append(assetResult[i]) } } }
讲解:self.allAsset 中全部是PHAsset对象,也就是后边我们要获取图片的对象,关于PHAsset对象的参数说明
- mediaType 四个枚举值 image video audio unknown
- pixelWidth 图片原始宽 pixelHeight 图片原始高
- creationDate 创建时间
- location 当时拍摄图片的地理坐标 可能是 nil 用的时候小心
- localIdentifier 这个非常重要 所有照片的一个唯一标识 并且还可以通过这个表示获得PHAsset对象,进而获得图片 稍后会讲如何获取图片
- duration 视频的话 会有个视频时长
第二步 通过localIdentifier 和 PHAsset如何获取图片
获取图片需要用到这几个类
- PHImageRequestOptions
- PHCachingImageManager
- PHImageManager
先说 PHImageRequestOptions 是我们请求图片时的可选配置
let option:PHImageRequestOptions = PHImageRequestOptions() option.deliveryMode = .fastFormat option.resizeMode = .fast option.isSynchronous = true option.isNetworkAccessAllowed = true
讲解PHImageRequestOptions相关配置
deliveryMode有 opportunistic highQualityFormat fastFormat三个枚举值 其中我常用的就是fastFormat和 highQualityFormat fastFormat是系统以最快的速度给我们返回一张看起来还可以的图片当对图片的获取速度有要求时可以使用。 highQualityFormat将会返回原图 当对图片质量要求高的时候可以用。
resizeMode有 fast exact none 跟deliveryMode差不多 都是根据对图片的要求 做尺寸的上缩减 fast 最快 exact有targetSize时使用 none 不缩减
isSynchronous同步获取还是异步获取,这里我选择同步获取 获取图片的回调方法只会执行一次 如果设置成异步获取,那么获取图片的回调会执行多次 true是同步获取
isNetworkAccessAllowed这个就是最蛋疼的iCloud 图片,当图片在iCloud中时 只有这个参数设置成true 才能获取到图片,每次做到这的时候 都忍不住想骂苹果。
通过PHAsset 获取图片
Photokit 框架 把 缩略图去掉了 但是直接拿图片的话 如果只有几张没问题,速度还可以,但是如果很多 那就不是1s能衡量的了 所以这里引出了 PHCachingImageManager,可以先通过PHCachingImageManager拿到一张很迷糊的图,然后再通过PHImageManager拿到相应清晰度可接受的图 具体看代码
片段一
asset 就是 PHAsset对象 拿到缓存图 这个还是比较快的 PHCachingImageManager.default().requestImage(for: asset, targetSize: CGSize(width: WIDTH-40, height: WIDTH-40), contentMode: .aspectFill, options: option, resultHandler: { (result, info) in if result != nil{ completeCache(result!) } }) 拿到可接受清晰度的图 速度一般 PHImageManager.default().requestImageData(for: asset, options: option) { (data, dataUi, aa, dic) in if data != nil{ completeReal(data!) } }
如何根据localIdentifier获得图片呢 直接看代码
let result = PHAsset.fetchAssets(withLocalIdentifiers: idents, options: nil) result.enumerateObjects { (asset, index, stop) in }
注意 这里的withLocalIdentifiers 是一个数组 通过enumerateObjects可以获得PHAsset 数组中有几个localIdentifier 就会回调几次 然后再用片段一中的代码获取图片
如何解决iCloud的问题
由于iCloud 是存在苹果服务器上的 需要网络下载,所以谁也没有办法像加载本地图片那样快速,只能通过一些间接的方法解决,我的方法是增加进度显示,类似相册中加载iCloud图片的进度条。那么如何获取进度
获取iCloud 下载进度
其实也很简单 还记得刚才的PHImageRequestOptions吧 只需要加上这段代码就可以了
option.progressHandler = {(pro,error,data,stop) in 这里的pro 就是每张图片的进度 只需要设置一个值 定时刷新UI就好了 }
进度条显示
画外圆 centerPoint 圆心 radius 半径 let path = UIBezierPath.init(arcCenter: CGPoint(x: centerPoint, y: centerPoint), radius: radius, startAngle: 0, endAngle: .pi*2, clockwise: true) let shapeLayer = CAShapeLayer() shapeLayer.path = path.cgPath shapeLayer.strokeColor = UIColor.white.cgColor shapeLayer.lineWidth = 2 shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.shadowColor = UIColor.black.cgColor shapeLayer.shadowOffset = CGSize(width: 0, height: 0) shapeLayer.shadowRadius = 3 shapeLayer.shadowOpacity = 0.3 self.layer.addSublayer(shapeLayer)
画内圆 let subPath = UIBezierPath.init(arcCenter: CGPoint(x: centerPoint, y: centerPoint), radius: radius, startAngle: .pi*(-1/2), endAngle: CGFloat(.pi*(progress*2-1/2)), clockwise: true) subPath.addLine(to: CGPoint(x: centerPoint, y: centerPoint)) let subLayer = CAShapeLayer() subLayer.path = subPath.cgPath subLayer.lineWidth = 0 subLayer.fillColor = UIColor.white.cgColor self.layer.addSublayer(subLayer)
关于图片上传
我们公司上传图片采用的是七牛云 图片上传很简单,这里只说视频上传的一个坑 ,由于iCloud的存在 所以视频上传要分开来上传
通过let resource = PHAssetResource.assetResources(for: showAsset) showAsset是PHAsset对象
let resource = PHAssetResource.assetResources(for: model.showAsset) if resource.first != nil{ let first = resource.first! if first.value(forKey: "locallyAvailable") != nil && first.value(forKey: "locallyAvailable") as? Bool != nil{ let isLocal = first.value(forKey: "locallyAvailable")! as! Bool if isLocal{ // 本地可用 上传七牛用PHAsset的方式 } else { // 本地不可用 3ff7 do{ let data = try Data(contentsOf: model.avUrl) 上传七牛用Data的方式 } catch{ fail() } } } }
总结
在网上找了很多例子 但是大多数都不全,都不能完全解决问题,今天就将自己整理的代码粘贴出来,关于相册的相关问题 应该可以解决95%的问题,希望对你有帮助。
- iOS开发:调用系统自带相机以及获取相册照片的功能实现
- iOS 获取系统相册全部照片以及照片信息
- IOS获取系统相册中照片的示例代码
- iOS - 获取系统相册照片名称,路径以及各项信息
- iOS开发-调用系统相机和相册获取照片示例
- 系统处理广播流程+三行代码获取注册了特定广播的所有接收者
- ios-获取系统相簿里边的所有照片
- iOS 使用photokit框架获取系统相册,实现多选,全选。
- iOS开发获取系统相册资源,自定义相册(PhotoKit)
- ios-获取系统相簿里边的所有照片
- iOS-获取系统相簿里边的所有照片
- sql 2000以及2005以上获取数据库中所有的表(不包括系统表)
- iOS 开发调用相机以及获取相册照片功能
- iOS图片处理(一)————调用系统相机和相册获取图片,给相机添加自定义覆盖物
- iOS 开发调用相机以及获取相册照片功能
- iOS 系统相册获取图片,保存图片,以及剪切图片
- iOS判断应用是否获取到系统相机 相册的授权 以及如何请求授权
- iOS从系统相册选取多张照片示例代码
- ios-获取系统相簿里边的所有照片
- iOS图片处理(一)调用系统相机和相册获取图片,给相机添加自定义覆盖物