import Flutter import Photos import UIKit public class ClassifyPhotoPlugin: NSObject, FlutterPlugin { var photoClassifier = ClassifyPhoto() public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "classify_photo", binaryMessenger: registrar.messenger()) let instance = ClassifyPhotoPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { print("iOS: Received method call: \(call.method)") switch call.method { case "getPhoto": self.getPhoto(flutterResult: result) case "getPlatformVersion": result("iOS " + UIDevice.current.systemVersion) default: result(FlutterMethodNotImplemented) } } private func getPhoto(flutterResult: @escaping FlutterResult) { DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self else { return } let fetchOptions = PHFetchOptions() let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions) photoClassifier.classifyPhotos( assets: allPhotos, progressHandler: { (stage, progress) in print("Progress: \(stage) - \(progress)") }, completion: { result in var resultData: [[String: Any]] = [] let mainGroup = DispatchGroup() // 处理截图 mainGroup.enter() self.processPhotoGroup(assets: result.screenshots, groupName: "screenshots", sizeInfo: result.screenshotsSize) { groupData in if !groupData.isEmpty { resultData.append(["group": groupData, "type": "screenshots"]) } mainGroup.leave() } // 处理相似照片组 for photoGroup in result.similarPhotos { mainGroup.enter() self.processPhotoGroup(assets: photoGroup, groupName: "similar", sizeInfo: result.similarPhotosSize) { groupData in if !groupData.isEmpty { resultData.append(["group": groupData, "type": "similar"]) } mainGroup.leave() } } // 处理地点分组 for (location, assets) in result.locations { mainGroup.enter() self.processPhotoGroup(assets: assets, groupName: location, sizeInfo: result.locationsSize) { groupData in if !groupData.isEmpty { resultData.append(["group": groupData, "type": "location", "name": location]) } mainGroup.leave() } } // 处理人物分组 for (person, assets) in result.people { mainGroup.enter() self.processPhotoGroup(assets: assets, groupName: person, sizeInfo: result.peopleSize) { groupData in if !groupData.isEmpty { resultData.append(["group": groupData, "type": "people"]) } mainGroup.leave() } } mainGroup.notify(queue: .main) { print("Final result count: \(resultData.count)") flutterResult(resultData) } } ) } // 在后台队列处理照片 // DispatchQueue.global(qos: .userInitiated).async { // let fetchOptions = PHFetchOptions() // let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions) // var resultData: [[String: Any]] = [] // // // 创建一个组来存储所有照片 // var allPhotosGroup: [[String: Any]] = [] // let semaphore = DispatchSemaphore(value: 0) // // allPhotos.enumerateObjects { (asset, index, stop) in // // 获取图片的文件URL // let options = PHContentEditingInputRequestOptions() // options.isNetworkAccessAllowed = true // // asset.requestContentEditingInput(with: options) { (input, info) in // if let input = input, let url = input.fullSizeImageURL { // let photoInfo: [String: Any] = [ // "path": url.path, // "id": asset.localIdentifier, // "width": asset.pixelWidth, // "height": asset.pixelHeight, // "creationDate": asset.creationDate?.timeIntervalSince1970 ?? 0 // ] // allPhotosGroup.append(photoInfo) // } // semaphore.signal() // } // semaphore.wait() // } // // if !allPhotosGroup.isEmpty { // resultData.append(["group": allPhotosGroup]) // } // // // 在主线程返回结果 // DispatchQueue.main.async { // flutterResult(resultData) // } // } // // 在后台队列处理照片 // DispatchQueue.global(qos: .userInitiated).async { [weak self] in // guard let self = self else { return } // // let fetchOptions = PHFetchOptions() // let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions) // // photoClassifier.classifyPhotos( // assets: allPhotos, // progressHandler: { (stage, progress) in // // 进度更新 // }, // completion: { result in // var similarPhotos = result.similarPhotos // var resultData: [[String: Any]] = [] // // for photoGroup in similarPhotos { // var groupData: [[String: Any]] = [] // let semaphore = DispatchSemaphore(value: 0) // // for asset in photoGroup { // let options = PHImageRequestOptions() // options.deliveryMode = .highQualityFormat // options.isSynchronous = false // // PHImageManager.default().requestImage( // for: asset, // targetSize: CGSize(width: 800, height: 800), // contentMode: .aspectFit, // options: options // ) { image, info in // if let image = image, // let imageData = image.jpegData(compressionQuality: 0.8) { // let photoInfo: [String: Any] = [ // "data": FlutterStandardTypedData(bytes: imageData), // "id": asset.localIdentifier, // "width": image.size.width, // "height": image.size.height, // "creationDate": asset.creationDate?.timeIntervalSince1970 ?? 0 // ] // groupData.append(photoInfo) // } // semaphore.signal() // } // semaphore.wait() // } // // if !groupData.isEmpty { // resultData.append(["group": groupData]) // } // } // // // 在主线程返回结果 // DispatchQueue.main.async { // flutterResult(resultData) // } // } // ) // } } // 处理照片组的辅助方法 private func processPhotoGroup( assets: [PHAsset], groupName: String, sizeInfo: ClassifyPhoto.PhotoSizeInfo, completion: @escaping ([String: Any]) -> Void ) { let photoProcessGroup = DispatchGroup() var photosData: [[String: Any]] = [] for asset in assets { photoProcessGroup.enter() let options = PHContentEditingInputRequestOptions() options.isNetworkAccessAllowed = true asset.requestContentEditingInput(with: options) { (input, info) in defer { photoProcessGroup.leave() } if let input = input, let url = input.fullSizeImageURL { let photoInfo: [String: Any] = [ "path": url.path, "id": asset.localIdentifier, "width": asset.pixelWidth, "height": asset.pixelHeight, "creationDate": asset.creationDate?.timeIntervalSince1970 ?? 0 ] photosData.append(photoInfo) } } } photoProcessGroup.notify(queue: .main) { completion([ "photos": photosData, "totalSize": sizeInfo.totalSize, "count": sizeInfo.count ]) } } }