Swift iOS应用中AVAsset仅存入数组一个对象的问题排查
问题:视频数组被覆盖,无法保存多个视频内容
我用Swift开发iOS应用的照片和视频功能,图片功能正常,images和uiimages数组能正确填充。但视频有问题:即便imageIndex已经更新,videos和media数组始终只保存一个视频,新视频会覆盖旧的。视频的逻辑和图片完全一致,图片存入media([AnyObject])数组没问题,视频却不行,求原因。
相关代码
主视图代码
@State var images = [Image?](repeating: nil, count: 6) @State var uiimages = [UIImage?](repeating: nil, count: 6) @State var videos = [AVAsset?](repeating: nil, count: 6) @State var media = [AnyObject?](repeating: nil, count: 6) ... CaptureImageView(isShown: $showCaptureImageView, uiimage: $uiimages[imageIndex], image: $images[imageIndex], video: $videos[imageIndex], amedia: $media[imageIndex], camera: camera) .onAppear { self.disableButtons = true } .onDisappear { var nonNil:Int = 0 for media in media { if media != nil { nonNil += 1 } } self.nonNilImages = nonNil self.imageIndex = nonNil self.disableButtons = false print("-------") print("imageIndex: \(imageIndex)") print(videos) print(media) }
CaptureImageView 实现
import SwiftUI import AVFoundation struct CaptureImageView { @Binding var isShown: Bool @Binding var uiimage: UIImage? @Binding var image: Image? @Binding var video: AVAsset? @Binding var amedia: AnyObject? var camera: Bool func makeCoordinator() -> Coordinator { return Coordinator(isShown: $isShown, image: $image, uiimage: $uiimage, video: $video, amedia: $amedia) } } extension CaptureImageView: UIViewControllerRepresentable { func makeUIViewController(context: UIViewControllerRepresentableContext<CaptureImageView>) -> UIImagePickerController { let picker = UIImagePickerController() picker.delegate = context.coordinator if camera { if let mediaTypes = UIImagePickerController.availableMediaTypes(for: .camera) { picker.mediaTypes = mediaTypes } picker.sourceType = .camera } return picker } func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<CaptureImageView>) { } }
Coordinator 实现
import SwiftUI import AVFoundation class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate { @Binding var isCoordinatorShown: Bool @Binding var uiimageInCoordinator: UIImage? @Binding var imageInCoordinator: Image? @Binding var videoInCoordinator: AVAsset? @Binding var mediaInCoordinator: AnyObject? init(isShown: Binding<Bool>, image: Binding<Image?>, uiimage: Binding<UIImage?>, video: Binding<AVAsset?>, amedia: Binding<AnyObject?>) { _isCoordinatorShown = isShown _uiimageInCoordinator = uiimage _imageInCoordinator = image _videoInCoordinator = video _mediaInCoordinator = amedia } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { if let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL { let asset = AVAsset(url: videoURL) print(asset) videoInCoordinator = AVAsset(url: videoURL) mediaInCoordinator = AVAsset(url: videoURL) } else if let unwrapImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { // handle image pick uiimageInCoordinator = unwrapImage imageInCoordinator = Image(uiImage: unwrapImage) mediaInCoordinator = unwrapImage } isCoordinatorShown = false } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { isCoordinatorShown = false } }
问题分析与解决方案
核心问题1:临时视频文件被系统回收
UIImagePickerController返回的视频URL指向系统临时文件,控制器消失后该文件可能被系统删除,导致数组中存储的AVAsset变成无效引用,看起来像是被覆盖。而图片直接返回UIImage对象,数据保存在内存中不会丢失。
核心问题2:AnyObject数组的状态追踪问题
media数组类型为[AnyObject?],SwiftUI对抽象类型的状态变化追踪不够敏感,相比之下具体类型(如UIImage)的绑定更新更可靠。
次要问题:循环变量名冲突
onDisappear中的循环变量media与数组名media重名,虽语法合法但易导致逻辑混淆,可能引发计数错误。
具体修复步骤
- 复制视频到应用沙盒,避免临时文件被删除
修改Coordinator中视频处理代码,将视频复制到应用Documents目录确保持久化:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { if let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL { // 获取应用Documents目录 guard let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { isCoordinatorShown = false return } // 生成唯一文件名避免重复 let fileName = UUID().uuidString + ".mov" let destinationURL = documentsDir.appendingPathComponent(fileName) do { // 复制视频到沙盒 try FileManager.default.copyItem(at: videoURL, to: destinationURL) let asset = AVAsset(url: destinationURL) videoInCoordinator = asset mediaInCoordinator = asset } catch { print("视频复制失败:\(error.localizedDescription)") } } else if let unwrapImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { uiimageInCoordinator = unwrapImage imageInCoordinator = Image(uiImage: unwrapImage) mediaInCoordinator = unwrapImage } isCoordinatorShown = false }
- 替换
AnyObject数组为自定义枚举类型
定义枚举统一管理图片和视频,让SwiftUI更准确追踪状态变化:
// 自定义媒体类型枚举 enum MediaItem { case image(UIImage) case video(AVAsset) } // 更新State变量类型 @State var media = [MediaItem?](repeating: nil, count: 6)
同时修改CaptureImageView和Coordinator的绑定类型:
// CaptureImageView中修改amedia的绑定类型 @Binding var amedia: MediaItem? // Coordinator中修改mediaInCoordinator的绑定类型 @Binding var mediaInCoordinator: MediaItem? // Coordinator的初始化方法对应修改 init(isShown: Binding<Bool>, image: Binding<Image?>, uiimage: Binding<UIImage?>, video: Binding<AVAsset?>, amedia: Binding<MediaItem?>) { _isCoordinatorShown = isShown _uiimageInCoordinator = uiimage _imageInCoordinator = image _videoInCoordinator = video _mediaInCoordinator = amedia } // 修改didFinishPickingMediaWithInfo中的赋值逻辑 if let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL { // ... 沙盒复制代码 ... let asset = AVAsset(url: destinationURL) videoInCoordinator = asset mediaInCoordinator = .video(asset) } else if let unwrapImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { uiimageInCoordinator = unwrapImage imageInCoordinator = Image(uiImage: unwrapImage) mediaInCoordinator = .image(unwrapImage) }
- 修复循环变量名冲突
修改onDisappear中的循环变量名,避免与数组名重名:
.onDisappear { var nonNil:Int = 0 // 将循环变量改为element,避免与media数组重名 for element in media { if element != nil { nonNil += 1 } } self.nonNilImages = nonNil self.imageIndex = nonNil self.disableButtons = false print("-------") print("imageIndex: \(imageIndex)") print(videos) print(media) }
内容的提问来源于stack exchange,提问作者Joseph
相关产品推荐
相关产品推荐

