Swift:如何等待requestAVAsset完成后再添加至数组
解决PHAsset视频加载异步导致数据为空的问题
你遇到的核心问题是:requestAVAsset是异步回调模式,而你的函数是async函数,回调还没执行完就直接返回了albumAssets,导致视频的mediaasset和mediaduration是空值。图片能正常工作是因为你给requestImageOptions设置了isSynchronous = true,强制同步执行,但视频的请求没有这个设置(也没法设置同步),所以需要把异步回调转换成async/await模式来等待结果。
关键解决方案:将异步回调包装为async函数
我们可以给PHImageManager写一个扩展,把requestAVAsset(forVideo:options:resultHandler:)和requestImage(for:targetSize:contentMode:options:resultHandler:)包装成支持async/await的方法,这样就能在你的async函数里用await等待结果完成。
修改后的完整代码
import Photos import SwiftUI import AVFoundation // 给PHImageManager添加async扩展,适配回调转async/await extension PHImageManager { func requestImageAsync(for asset: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?) async -> UIImage? { await withCheckedContinuation { continuation in self.requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: options) { image, _ in continuation.resume(returning: image) } } } func requestAVAssetAsync(for asset: PHAsset, options: PHVideoRequestOptions?) async -> (AVAsset?, AVAudioMix?) { await withCheckedContinuation { continuation in self.requestAVAsset(forVideo: asset, options: options) { asset, audioMix, _ in continuation.resume(returning: (asset, audioMix)) } } } } func getAlbumAssets(albumName: String) async -> [AlbumAsset] { var albumAssets: [AlbumAsset] = [] let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate(format: "title = %@", albumName) let albumCollection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions) guard let targetAlbum = albumCollection.firstObject else { return albumAssets } let albumAssetsResult = PHAsset.fetchAssets(in: targetAlbum, options: nil) guard albumAssetsResult.count > 1 else { // 确保存在索引为1的资源 return albumAssets } let asset = albumAssetsResult.object(at: 1) // 图片请求配置(移除同步设置,改用async等待) let requestImageOptions = PHImageRequestOptions() requestImageOptions.version = .current requestImageOptions.resizeMode = .exact requestImageOptions.deliveryMode = .highQualityFormat requestImageOptions.isNetworkAccessAllowed = true requestImageOptions.progressHandler = { progress, error, stop, info in if progress == 1.0 { print("Downloaded Shared Image from iCloud") } else { print("\(progress)") } } // 视频请求配置 let requestVideoOptions = PHVideoRequestOptions() requestVideoOptions.version = .current requestVideoOptions.deliveryMode = .highQualityFormat requestVideoOptions.isNetworkAccessAllowed = true requestVideoOptions.progressHandler = { progress, error, stop, info in if progress == 1.0 { print("Downloaded Shared Video from iCloud") } else { print("\(progress)") } } let imageManager = PHImageManager() let imageSize = CGSize(width: asset.pixelWidth/2, height: asset.pixelHeight/2) var converted = asset.mediaType == .image ? Image("iva") : Image("vai") var mediaasset = "" var mediaduration = CMTimeGetSeconds(.zero) // 用await等待异步操作完成,确保数据就绪 if asset.mediaType == .image { if let image = await imageManager.requestImageAsync(for: asset, targetSize: imageSize, contentMode: .aspectFill, options: requestImageOptions) { converted = Image(uiImage: image) } } else if asset.mediaType == .video { let (avAsset, _) = await imageManager.requestAVAssetAsync(for: asset, options: requestVideoOptions) if let avaURLasset = avAsset as? AVURLAsset { mediaasset = avaURLasset.url.absoluteString mediaduration = CMTimeGetSeconds(avaURLasset.duration) } } // 修复原代码位置信息未赋值的问题 let location: CLLocationCoordinate2D if let assetLocation = asset.location { location = assetLocation.coordinate } else { location = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0) } // 所有数据准备完毕后,再创建AlbumAsset let newAsset = AlbumAsset( index: 0, album: albumName, name: asset.localIdentifier, createDate: asset.creationDate ?? Date(), asset: mediaasset, duration: mediaduration, image: converted, imageType: asset.mediaType, imageWidth: CGFloat(asset.pixelWidth), imageHeight: CGFloat(asset.pixelHeight), location: location, cover: false ) albumAssets.append(newAsset) return albumAssets } // 假设你的AlbumAsset结构体定义如下(可根据实际情况调整) struct AlbumAsset { let index: Int let album: String let name: String let createDate: Date let asset: String let duration: Double let image: Image let imageType: PHAssetMediaType let imageWidth: CGFloat let imageHeight: CGFloat let location: CLLocationCoordinate2D let cover: Bool }
核心修改点说明
- 回调转async/await:用
withCheckedContinuation将PHImageManager的异步回调API包装成支持await的方法,从根本上解决异步等待问题。 - 统一异步处理逻辑:移除图片请求的
isSynchronous = true,用await统一等待图片/视频加载完成,代码更规范。 - 确保数据就绪再创建对象:视频处理部分必须等
await拿到AVAsset后,才赋值相关字段,避免空值。 - 修复位置信息赋值漏洞:原代码中判断了位置但未给
location变量赋值,现在补上逻辑确保字段有效。
内容的提问来源于stack exchange,提问作者KentinCopa
相关产品推荐
相关产品推荐

