Skip to content

Instantly share code, notes, and snippets.

@AmatsuZero
Created July 25, 2018 11:28
Show Gist options
  • Save AmatsuZero/138ff0efe7f707d946271d6c2bb37f37 to your computer and use it in GitHub Desktop.
Save AmatsuZero/138ff0efe7f707d946271d6c2bb37f37 to your computer and use it in GitHub Desktop.
iCloud && Photos.framework
//
// PhotoManager.swift
// EShopHelper
//
// Created by Jiang,Zhenhua on 2018/7/25.
// Copyright © 2018年 Daubert. All rights reserved.
//
import Foundation
import Photos
class PhotoManager {
enum AlbumType {
case image, photo
}
static let shared = PhotoManager()
private(set) lazy var phCachingManager: PHCachingImageManager? = {
guard PHPhotoLibrary.authorizationStatus() == .authorized else {
return nil
}
return PHCachingImageManager()
}()
private(set) var assetCollections = [PHAssetCollection]()
let gifCache = NSCache<NSString, UIImage>()
private let imageCache = NSCache<NSString, UIImage>()
private lazy var imageREqOptions: PHImageRequestOptions = {
let opt = PHImageRequestOptions()
opt.resizeMode = .fast
opt.isNetworkAccessAllowed = false
return opt
}()
private var defaultReqOptions: PHImageRequestOptions!
private lazy var icloudImageReqOptions: PHImageRequestOptions = {
let opt = PHImageRequestOptions()
opt.deliveryMode = .highQualityFormat
opt.resizeMode = .fast
opt.isNetworkAccessAllowed = true
return opt
}()
func fetchResults(type: AlbumType) -> [PHAssetCollection] {
// 智能相册
let smartAlbum = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil)
// 用户相册
let userAlbum = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: nil)
return setFetchResults(fetchResults: [smartAlbum, userAlbum], type: type)
}
@discardableResult
func setFetchResults(fetchResults: [PHFetchResult<PHAssetCollection>], type: AlbumType) -> [PHAssetCollection] {
var collection = [PHAssetCollection]()
fetchResults.forEach {
$0.enumerateObjects { (object, index, stop) in
collection.append(object)
}
}
assetCollections = collection
return collection
}
class func assetOptions(mediaType: PHAssetMediaType, sorkKey: String, isAscending: Bool) -> PHFetchOptions {
let options = PHFetchOptions()
options.predicate = NSPredicate.init(format: "mediaType in %@", [mediaType])
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: isAscending)]
return options
}
func requestAssetCollection(subType: PHAssetCollectionSubtype) -> PHAssetCollection? {
var cameraCollection: PHAssetCollection?
for collection in assetCollections where collection.assetCollectionSubtype == subType {
cameraCollection = collection
}
// 解决有的手机上没有相机胶卷,则用最近添加文件夹
if cameraCollection == nil {
for collection in assetCollections where collection.assetCollectionSubtype == .smartAlbumRecentlyAdded {
cameraCollection = collection
}
}
return cameraCollection
}
func defaultImageReqOpt(isSync: Bool) -> PHImageRequestOptions {
defaultReqOptions = imageREqOptions
defaultReqOptions?.isSynchronous = isSync
return defaultReqOptions
}
/// 获取缩略图
@discardableResult
func requestThumbnailImage(asset: PHAsset,
isSync: Bool,
completionHandler: @escaping (UIImage?, [AnyHashable: Any]?) -> Void) -> PHImageRequestID? {
let option = defaultImageReqOpt(isSync: isSync)
let width = UIScreen.main.bounds.width * 0.6
let defaultSize = CGSize(width: width, height: width)
return requestImage(asset: asset,
targetSize: defaultSize,
contentMode: .aspectFit,
options: option,
completionHandler: completionHandler)
}
/// 获取iCloud缩略图
@discardableResult
func requestIcloudImage(asset: PHAsset, completionHandler: (UIImage, PHAsset) -> Void) -> PHImageRequestID {
return requestImageData(asset: asset, options: icloudImageReqOptions) { info in
if let data = info["imageData"] as? Data, let image = UIImage(data: data) {
completionHandler(image, asset)
}
}
}
/// 获取iCloud
@discardableResult
func requestIcloudPreviewImage(asset: PHAsset,
option: PHImageRequestOptions?,
completionHandler: @escaping (UIImage?, [AnyHashable: Any]?) -> Void) -> PHImageRequestID? {
let opt = option ?? icloudImageReqOptions
return requestImage(asset: asset, targetSize: UIScreen.main.bounds.size, contentMode: .aspectFill, options: opt, completionHandler: completionHandler)
}
/// 获取原图
@discardableResult
func requestOriginalImage(asset: PHAsset,
isSync: Bool,
limit: UInt,
completionHandler: @escaping (UIImage?, [AnyHashable: Any]?) -> Void) -> PHImageRequestID? {
let opt = defaultImageReqOpt(isSync: isSync)
return requestImage(asset: asset, targetSize: limitSIze(asset: asset, limit: CGFloat(limit)), options: opt, completionHandler: completionHandler)
}
/// 获取上传用的图
@discardableResult
func requestUploadImage(asset: PHAsset,
isSync: Bool,
limit: UInt,
completionHandler: @escaping (UIImage?, [AnyHashable: Any]?) -> Void) -> PHImageRequestID? {
let options = defaultImageReqOpt(isSync: isSync)
return requestImage(asset: asset, targetSize: limitSIze(asset: asset, limit: CGFloat(limit)), options: options, completionHandler: completionHandler)
}
/// 自己指定获取image
@discardableResult
func requestImage(asset:PHAsset,
targetSize: CGSize,
contentMode: PHImageContentMode = .aspectFill,
options: PHImageRequestOptions,
completionHandler: @escaping (UIImage?, [AnyHashable: Any]?) -> Void) -> PHImageRequestID? {
let id = identifier(with: asset, targetSize: targetSize) as NSString
if let cache = imageCache.object(forKey: id) {
completionHandler(cache, nil)
return PHInvalidImageRequestID
} else {
return phCachingManager?.requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: options) { [weak self] (image, info) in
if image == nil, asset.mediaType != .video {
return completionHandler(nil, info)
}
if let img = image {
//第一次的低清图,只有展示缩略图和预览图可用,上传时不需要
completionHandler(img, info)
}
if let dict = info,
dict[PHImageCancelledKey] as? Bool == false,
dict[PHImageErrorKey] == nil,
dict[PHImageResultIsDegradedKey] as? Bool == false {
self?.imageCache.setObject(image!, forKey: id)
completionHandler(image!, info)
}
}
}
}
/// 获取原图方法
@discardableResult
func requestImageData(asset: PHAsset,
options: PHImageRequestOptions,
completionHandler: @escaping (UIImage?) -> Void) -> PHImageRequestID? {
let id = identifier(with: asset, targetSize: .zero) as NSString
if let image = imageCache.object(forKey: id) {
completionHandler(image)
} else {
return phCachingManager?.requestImageData(for: asset, options: options) { [weak self] (imageData, dataUTI, orientation, info) in
guard let `self` = self else { return }
if imageData == nil {
self.requestImageData(asset: asset, options: self.icloudImageReqOptions, completionHandler: completionHandler)
} else {
let img = UIImage(data: imageData!)
if asset.mediaType == .image, let img = img {
self.imageCache.setObject(img, forKey: id)
}
completionHandler(img)
}
}
}
}
/// 根据asset获取video的avplayerItem
@discardableResult
func requestPlayerItemForVideo(asset: PHAsset,
options: PHVideoRequestOptions,
completionHandler: @escaping (AVPlayerItem?, [AnyHashable: Any]?) -> Void) -> PHImageRequestID? {
return phCachingManager?.requestPlayerItem(forVideo: asset, options: options, resultHandler: completionHandler)
}
/// 可用于requestImageWithAsset之前,提前缓存图片,target一般尺寸小于真实获取尺寸
func startCachingImage(assets: [PHAsset],
targetSize: CGSize,
contentMode: PHImageContentMode,
options: PHImageRequestOptions) {
}
func stopCachingImageForAssets(assets: [PHAsset],
targetSize: CGSize,
contentMode: PHImageContentMode,
options: PHImageRequestOptions) {
}
func stopCachingImagesForAllAssets() {
phCachingManager?.stopCachingImagesForAllAssets()
}
}
private func identifier(with asset: PHAsset, targetSize: CGSize) -> String {
return "\(asset.localIdentifier)\(targetSize)"
}
private func limitSIze(asset: PHAsset, limit: CGFloat) -> CGSize {
var width = CGFloat(asset.pixelWidth)
var height = CGFloat(asset.pixelHeight)
let ratio = width / height
if width > limit || height > limit {
if (width > height) {
width = limit
height = width / ratio
} else {
height = limit
width = height * ratio
}
}
return CGSize(width: width, height: height)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment