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..

讲解:self.allAsset 中全部是PHAsset对象,也就是后边我们要获取图片的对象,关于PHAsset对象的参数说明

  1. mediaType 四个枚举值 image video audio unknown
  2. pixelWidth 图片原始宽 pixelHeight 图片原始高
  3. creationDate 创建时间
  4. location 当时拍摄图片的地理坐标 可能是 nil 用的时候小心
  5. localIdentifier 这个非常重要 所有照片的一个唯一标识 并且还可以通过这个表示获得PHAsset对象,进而获得图片 稍后会讲如何获取图片
  6. duration 视频的话 会有个视频时长

第二步 通过localIdentifier 和 PHAsset如何获取图片

获取图片需要用到这几个类

  1. PHImageRequestOptions
  2. PHCachingImageManager
  3. 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 {
                    // 本地不可用
                    do{
                        let data = try Data(contentsOf: model.avUrl)
                       上传七牛用Data的方式
                    }
                    catch{
                        fail()
                    }
                }
            }
        }

总结

在网上找了很多例子 但是大多数都不全,都不能完全解决问题,今天就将自己整理的代码粘贴出来,关于相册的相关问题 应该可以解决95%的问题,希望对你有帮助。

你可能感兴趣的:(iOS)