Created
July 25, 2018 11:28
-
-
Save AmatsuZero/138ff0efe7f707d946271d6c2bb37f37 to your computer and use it in GitHub Desktop.
iCloud && Photos.framework
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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