import Foundation import Photos /** * 相册公用方法类 */ class JYAlbumHelpModel: NSObject { /// 获取所有相册 /// /// - Returns: 返回相册列表集合 static func getAllAlbumList() -> [PHAssetCollection] { let smartAlbums: PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil) guard smartAlbums.count > 0 else { return [] } var tempArr: [PHAssetCollection] = [] ///创建的相册文件夹 let topLeveUserCollections = PHCollectionList.fetchTopLevelUserCollections(with: nil) if topLeveUserCollections.count > 0 { for i in 0 ... topLeveUserCollections.count - 1 { let collection = topLeveUserCollections[i] if collection.isKind(of: PHAssetCollection.self) , let assetCollection = collection as? PHAssetCollection { let fetchResult = PHAsset.fetchAssets(in: assetCollection, options: nil) var isContains: Bool = false if fetchResult.count > 0 { for i in 0 ... fetchResult.count - 1 { let asste = fetchResult[i] if asste.mediaType != .video { isContains = true break } } } if isContains { tempArr.append(assetCollection) DDLOG(message:"11111 assetCollectionSubtype =\( String(describing: assetCollection.localizedTitle)), assetCollectionType = \(assetCollection.assetCollectionType.rawValue), assetCollectionSubtype = \(assetCollection.assetCollectionSubtype.rawValue)") } } } } ///得到的结果 和我的相薄 是反的, 需要倒序 排列一下 tempArr = tempArr.reversed() ///系统创建的文件夹 for i in 0 ... smartAlbums.count - 1 { let collection = smartAlbums[i] as PHCollection if collection.isKind(of: PHAssetCollection.self) , let assetCollection = collection as? PHAssetCollection , assetCollection.assetCollectionType != .moment { let fetchResult = PHAsset.fetchAssets(in: assetCollection, options: nil) var isContains: Bool = false if fetchResult.count > 0 { for i in 0 ... fetchResult.count - 1 { let asste = fetchResult[i] if asste.mediaType != .video { isContains = true break } } } ///最近项目/相机胶卷/所有照片放到第一位 if isContains { if assetCollection.localizedTitle == "相机胶卷" || assetCollection.localizedTitle == "所有照片" || assetCollection.localizedTitle == "最近项目" { tempArr.insert(assetCollection, at: 0) ///不添加 已删除和已隐藏 }else if assetCollection.localizedTitle == "最近删除" || assetCollection.localizedTitle == "已隐藏"{ }else{ tempArr.append(assetCollection) DDLOG(message:"22222 localizedTitle =\( String(describing: assetCollection.localizedTitle)), assetCollectionType = \(assetCollection.assetCollectionType.rawValue), assetCollectionSubtype = \(assetCollection.assetCollectionSubtype.rawValue)") } } } } ///个人收藏放到第二位 if tempArr.count > 2 , let index = tempArr.firstIndex(where: {$0.localizedTitle == "个人收藏"}) , index != 1 { let temp = tempArr.remove(at: index) tempArr.insert(temp, at: 1) } ///最近项目和最近添加一样的, 删除最近添加 for (index, assetCollection) in tempArr.enumerated(){ if assetCollection.localizedTitle == "最近添加", let firstTitle = tempArr.first?.localizedTitle, firstTitle == "最近项目"{ tempArr.remove(at: index) } } tempArr.forEach { (assetCollection) in DDLOG(message:"reversed tempArr444 =\( String(describing: assetCollection.localizedTitle))") } return tempArr } //// 获取多张图片 static func asyncGetImageArr(by assetArr: [PHAsset] ,tageSize: CGSize = CGSize(width: JYScreenWidth * 3 , height: JYScreenHeight * 3), resultHandle:((_ imageArr: [UIImage]) -> Void)? , progressHandle: ((_ progress: Double , _ err: String?) -> Void)?) { let quenueGroup = DispatchGroup() let semphore = DispatchSemaphore(value: 1) let quenue = DispatchQueue.global(qos: DispatchQoS.QoSClass.background) var temImageArr: [UIImage] = [] for asset in assetArr { quenueGroup.enter() quenue.async{ semphore.wait() DDLOG(message: "开始获取") asyncGetImage(by: asset, tageSize: tageSize, resultHandle: { (image) in if let res = image{ temImageArr.append(res) }else { DispatchQueue.main.async { progressHandle?(0, "获取图片失败") } } DDLOG(message: "获取成功") semphore.signal() quenueGroup.leave() }, progressHandle: { (progress, err) in DispatchQueue.main.async { progressHandle?(progress , err?.localizedDescription) } }) } } quenueGroup.notify(queue: .main) { DDLOG(message: Thread.current) resultHandle?(temImageArr) } } /// 获取单张指定大小的图片 static func asyncGetImage(by asset: PHAsset ,tageSize: CGSize , resultHandle:((_ imageArr: UIImage?) -> Void)? , progressHandle: ((_ progress: Double , _ err: Error?) -> Void)?) { let option = PHImageRequestOptions() option.resizeMode = .fast option.deliveryMode = .highQualityFormat option.isNetworkAccessAllowed = true option.progressHandler = { (progress ,err , stop , info) in progressHandle?(progress,err) } PHImageManager.default().requestImage(for: asset, targetSize: tageSize, contentMode: PHImageContentMode.aspectFit, options: option) { (result, info) in if let degrade = info?[PHImageResultIsDegradedKey] as? Bool , degrade == false , let image = result { resultHandle?(image) }else if result == nil { resultHandle?(nil) JYLogsModel.JYLog(logType: JYLogsModel.JYLogType.errorType, logStr: "获取指定大小本地相册图片失败") } } } /// 获取单张原图data static func asynGetOriginImageData(asset: PHAsset , resulrHandle:((_ data: Data?) -> Void)?) { let option = PHImageRequestOptions() option.resizeMode = .fast option.deliveryMode = .highQualityFormat option.isNetworkAccessAllowed = true PHImageManager.default().requestImageData(for: asset, options: option) { (data, dataUTI, orientation, info) in if let degrade = info?[PHImageResultIsDegradedKey] as? Bool , degrade == false { if let _data = data, UIImage(data: _data) == nil { asyncGetImage(by: asset, tageSize: CGSize(width: JYScreenWidth * 2, height: JYScreenHeight * 2), resultHandle: { (image) in if let data = image?.jpegData(compressionQuality: 1.0) { resulrHandle?(data) }else { resulrHandle?(nil) } }, progressHandle: nil) }else { resulrHandle?(data) } }else { resulrHandle?(data) JYLogsModel.JYLog(logType: JYLogsModel.JYLogType.errorType, logStr: "获取单张原图失败") } } } /// 处理imageData(heic) /// /// - Parameter imageData: 图片二进制流 /// - Returns: 压缩后的图片二进制流 static func handleHEICImageData(imageData: Data , type: JYSelectPhotoType) -> Data { if #available(iOS 11.0, *) { if let sour = CGImageSourceCreateWithData(imageData as CFData, nil) , let _type = CGImageSourceGetType(sour) as String? { if _type == AVFileType.heic.rawValue || _type == AVFileType.heif.rawValue { if let ciimage = CIImage(data: imageData) , let color = ciimage.colorSpace { let context = CIContext() if let jpgData = context.jpegRepresentation(of: ciimage, colorSpace: color, options:[: ]) { return scaleImage(imageData: jpgData , type: type) } }else { if let image = UIImage(data: imageData) , let _jpgData = image.jpegData(compressionQuality: 1.0) { return scaleImage(imageData: _jpgData , type: type) } } } } } return scaleImage(imageData: imageData , type: type) } /// 不同图片压缩的大小不一致 /// /// - Parameter imageData: 图片二进制流 /// - Returns: 压缩后的图片 private static func scaleImage(imageData: Data , type: JYSelectPhotoType) -> Data { guard type != .uploadCertificateOriginType else { // 压缩证件 return JYAlbumHelpModel.handleCerImage(data: imageData) } let imageCount = Double(imageData.count) if imageCount > 1.5 * 1024 * 1024 , imageCount <= 3.0 * 1024 * 1024 { /// 压缩到1.0M let data = compressBySizeWithMaxLength(maxCount: 1024 * 1024, imageData: imageData) DDLOG(message: "压缩1后的大小为:\(data.count)") return data }else if imageCount > 3.0 * 1024 * 1024 , imageCount <= 8 * 1024 * 1024 { /// 压缩到1.2M let data = compressBySizeWithMaxLength(maxCount: Int64(1.2 * 1024 * 1024), imageData: imageData) DDLOG(message: "压缩2后的大小为:\(data.count)") return data }else if imageCount > 8 * 1024 * 1024 { // 压缩到1.5M let data = compressBySizeWithMaxLength(maxCount: Int64(1.5 * 1024 * 1024), imageData: imageData) DDLOG(message: "压缩3后的大小为:\(data.count)") return data } return imageData } /// 二分法压缩图片 private static func compressBySizeWithMaxLength(maxCount: Int64 , imageData: Data) -> Data { guard imageData.count > maxCount else { return imageData } var data = imageData if let resultImage = UIImage(data: imageData) { var min: CGFloat = 0.0 ; var max: CGFloat = 1.0 ; var compression: CGFloat = 1.0 for _ in 0 ... 6 { compression = (min + max)/2 data = resultImage.jpegData(compressionQuality: compression) ?? data // 此处设置误差范围为0 - 0.1 if Double(data.count) > Double(maxCount) * 1.1 { max = compression }else if Double(data.count) < Double(maxCount) { min = compression }else { break } } } return data } /// 处理证件图片大小 private static func handleCerImage(data: Data) -> Data{ guard data.count > 1024 * 1024 else { return data } let s = CGFloat(sqrtf(Float(data.count)/Float((1024 * 1024)))) if let image = UIImage(data: data) , let newImage = image.jy.imageWithScale(width: image.size.width/s){ DDLOG(message: "原大小:\(data.count) , 尺寸压缩:\(s) ,原图尺寸:\(image.size) , 新图片尺寸:\(newImage.size)") if let newData = newImage.jpegData(compressionQuality: 1.0) { DDLOG(message: "新图片大小:\(newData.count)") return compressBySizeWithMaxLength(maxCount: 1024 * 1024, imageData: newData) } } return data } }