SwiftUI图片编辑器使用Mantis库相机选图时因内存问题崩溃
iOS SwiftUI图片编辑器相机选图崩溃(内存不足)
我正在开发一款基于SwiftUI的iOS图片编辑器,使用Mantis库实现图片裁剪功能。目前遇到一个问题:从系统图库选择图片时所有功能正常,但通过相机拍摄选择图片后,应用会直接崩溃,报错信息如下:
Message from debugger: Terminated due to memory issue
以下是我的代码:
HomePage 视图
import SwiftUI import Mantis struct HomePage: View { @State var bgImage :UIImage? @State var camIsClicked = false @State private var isImagePickerDisplay = false @State var sourceType: UIImagePickerController.SourceType? @State var showCropper = false @State var gotoImageEdit = false @State private var cropShapeType: Mantis.CropShapeType = .rect @State private var presetFixedRatioType: Mantis.PresetFixedRatioType = .canUseMultiplePresetFixedRatio() var camAlertView: some View{ VStack { VStack{ VStack(spacing: Constants.device == .pad ? 20:10){ Text("选择来源!") .foregroundColor(Color.red) .fontWeight(.bold) .font(Constants.device == .pad ? .largeTitle:.title2) Divider() .frame(width: Constants.width*0.65 , height: Constants.device == .pad ? 3.5:2) .background(Color.red) } .padding(.top) Spacer() Button { sourceType = .photoLibrary isImagePickerDisplay.toggle() } label: { Text("图库") .fontWeight(.bold) .font(Constants.device == .pad ? .title:.title3) .frame(width: Constants.width*0.34, height: Constants.device == .pad ? 70:40) .background(Color.red) .cornerRadius(Constants.device == .pad ? 35:20) } Spacer() Button { sourceType = .camera isImagePickerDisplay.toggle() } label: { Text("相机") .fontWeight(.bold) .font(Constants.device == .pad ? .title:.title3) .frame(width: Constants.width*0.34, height: Constants.device == .pad ? 70:40) .background(Color.red) .cornerRadius(Constants.device == .pad ? 35:20) } Spacer() } .frame(width: Constants.width*0.65, height: Constants.height*0.39) .background(Color.white) .cornerRadius(20) .foregroundColor(.white) } .frame(width: Constants.width, height: Constants.height) .background(Color.black .opacity(0.8) .ignoresSafeArea() .onTapGesture { camIsClicked = false }) } var body: some View { ZStack{ NavigationLink( destination: ImageEditPage(bgImage: $bgImage), isActive: $gotoImageEdit) { EmptyView() } VStack{ Button { camIsClicked.toggle() } label: { Text("选择图片") .padding() .background(Color.green) .cornerRadius(30) } } if camIsClicked{ camAlertView } if isImagePickerDisplay{ if sourceType == .photoLibrary{ SUImagePickerView(sourceType: .photoLibrary, image: $bgImage, isPresented: $isImagePickerDisplay, camIsClicked: $camIsClicked, bgImageIsSelected: $showCropper) }else{ SUImagePickerView(sourceType: .camera, image: $bgImage, isPresented: $isImagePickerDisplay, camIsClicked: $camIsClicked, bgImageIsSelected: $showCropper) } } } .navigationBarHidden(true) .fullScreenCover(isPresented: $showCropper, content: { ImageCropper(gotoImageEdit: $gotoImageEdit, image: $bgImage,cropShapeType: $cropShapeType,presetFixedRatioType: $presetFixedRatioType) .ignoresSafeArea() }) } } struct HomePage_Previews: PreviewProvider { static var previews: some View { HomePage() } }
ImageCropper 结构体(UIViewControllerRepresentable)
import SwiftUI import Mantis struct ImageCropper: UIViewControllerRepresentable { @Binding var gotoImageEdit : Bool @Binding var image: UIImage? @Binding var cropShapeType: Mantis.CropShapeType @Binding var presetFixedRatioType: Mantis.PresetFixedRatioType @Environment(\.presentationMode) var presentationMode class Coordinator: CropViewControllerDelegate { func cropViewControllerDidImageTransformed(_ cropViewController: CropViewController) { } var parent: ImageCropper init(_ parent: ImageCropper) { self.parent = parent } func cropViewControllerDidCrop(_ cropViewController: CropViewController, cropped: UIImage, transformation: Transformation, cropInfo: CropInfo) { parent.image = cropped print("transformation is \(transformation)") parent.gotoImageEdit = true parent.presentationMode.wrappedValue.dismiss() } func cropViewControllerDidCancel(_ cropViewController: CropViewController, original: UIImage) { parent.presentationMode.wrappedValue.dismiss() } func cropViewControllerDidFailToCrop(_ cropViewController: CropViewController, original: UIImage) { } func cropViewControllerDidBeginResize(_ cropViewController: CropViewController) { } func cropViewControllerDidEndResize(_ cropViewController: CropViewController, original: UIImage, cropInfo: CropInfo) { } } func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIViewController(context: Context) -> CropViewController { var config = Mantis.Config() config.cropViewConfig.cropShapeType = cropShapeType config.presetFixedRatioType = presetFixedRatioType let cropViewController = Mantis.cropViewController(image: image!, config: config) cropViewController.delegate = context.coordinator return cropViewController } func updateUIViewController(_ uiViewController: CropViewController, context: Context) { } }
问题分析与解决方案
相机拍摄的图片分辨率通常远高于图库中压缩后的图片,直接传入Mantis裁剪控制器会导致内存占用过高崩溃。可以从以下几个方面优化:
- 压缩相机拍摄的图片
在相机选图完成后,先对UIImage进行压缩处理,降低图片分辨率和体积,再传入裁剪控制器。例如添加一个图片压缩方法:
func compressImage(_ image: UIImage, targetSize: CGSize = CGSize(width: 1080, height: 1920)) -> UIImage? { let widthRatio = targetSize.width / image.size.width let heightRatio = targetSize.height / image.size.height let scaleRatio = min(widthRatio, heightRatio) let newSize = CGSize(width: image.size.width * scaleRatio, height: image.size.height * scaleRatio) UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) image.draw(in: CGRect(origin: .zero, size: newSize)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage }
在SUImagePickerView获取到相机图片后,调用该方法压缩,再赋值给bgImage。
- 避免强制解包
image!
在ImageCropper的makeUIViewController方法中,直接使用image!会存在崩溃风险(如果图片处理失败导致image为nil),应该添加可选绑定判断:
guard let image = image else { // 处理图片为空的情况,比如返回一个空的控制器或者提示用户 return CropViewController() } let cropViewController = Mantis.cropViewController(image: image, config: config)
- 检查Mantis库版本
确保使用的是Mantis库的最新版本,新版本可能已经修复了相机图片内存占用过高的问题。
内容的提问来源于stack exchange,提问作者Iftikhar Hussain
相关产品推荐
相关产品推荐

