在 SwiftUI 中使用 TOCropViewController 选取图像时关闭图像选取和裁剪视图时出错

Error dismissing image picking and cropping views when using TOCropViewController to pick images in SwiftUI

提问人:Nathen Antony 提问时间:7/26/2023 更新时间:7/26/2023 访问量:50

问:

我的 Swift 应用程序使用 TOCropViewController 来选取图像。处理图像拾取和裁剪的结构称为 ImagePicker。

从中调用 ImagePicker 的视图是工作表视图。当我调用 ImagePicker 时,一切正常,图像拾取屏幕出现,我按下取消按钮,图像拾取屏幕被很好地关闭。

当我选择图像时,我会转到裁剪视图。从这里开始,如果我按取消,一切都会按我想要的方式工作,但是如果我按完成,我选择和裁剪的图像不会被发送回我想要的调用 ImagePicker 的视图。

除了我的图像没有被发回的问题外,我的 ImagePicker 视图,包括裁剪视图和图像选取视图,它们都不能很好地解决。

由于我调用 ImagePicker 的视图是工作表,因此当我单击“完成”时,工作表会升起,并且我可以看到工作表视图下方的内容。屏幕上的所有内容都变得无法点击。

这是我从中调用 ImagePicker 的工作表视图在我单击完成后的样子

请帮我解决这个问题。

这是ImagePicker.swift文件:

import SwiftUI
import UIKit
import TOCropViewController

struct ImagePicker: UIViewControllerRepresentable {
    @Environment(\.presentationMode) var presentationMode
    @Binding var image: UIImage?
    var didFinishPicking: () -> Void
    var aspectRatioPreset: TOCropViewControllerAspectRatioPreset

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate, TOCropViewControllerDelegate {
        var originalPicker: UIImagePickerController?
        @Binding var image: UIImage?
        var didFinishPicking: () -> Void
        var aspectRatioPreset: TOCropViewControllerAspectRatioPreset
        var presentingViewController: UIViewController?

        init(image: Binding<UIImage?>, didFinishPicking: @escaping () -> Void, aspectRatioPreset: TOCropViewControllerAspectRatioPreset, presentingViewController: UIViewController?) {
            _image = image
            self.didFinishPicking = didFinishPicking
            self.aspectRatioPreset = aspectRatioPreset
            self.presentingViewController = presentingViewController
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            guard let image = info[.originalImage] as? UIImage else {
                picker.dismiss(animated: true, completion: nil)
                return
            }

            let cropViewController = TOCropViewController(image: image)
            cropViewController.delegate = self
            cropViewController.aspectRatioPreset = aspectRatioPreset
            cropViewController.aspectRatioLockEnabled = true
            originalPicker = picker
            picker.present(cropViewController, animated: true, completion: nil)
        }

        func cropViewController(_ cropViewController: TOCropViewController, didCropTo croppedImage: UIImage, withRect cropRect: CGRect, angle: Int) {
            self.image = croppedImage
            cropViewController.dismiss(animated: true) {
                self.originalPicker?.dismiss(animated: true, completion: {
                    self.didFinishPicking()
                })
            }
        }

        func cropViewController(_ cropViewController: TOCropViewController, didFinishCancelled cancelled: Bool) {
            if cancelled {
                cropViewController.dismiss(animated: true) {
                    self.originalPicker?.dismiss(animated: true, completion: {
                        self.didFinishPicking()
                    })
                }
            }
        }
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(image: $image, didFinishPicking: didFinishPicking, aspectRatioPreset: aspectRatioPreset, presentingViewController: UIApplication.shared.windows.first?.rootViewController)
    }

    func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
    }
}

struct ImagePicker_Previews: PreviewProvider {
    static var previews: some View {
        ImagePicker(image: .constant(UIImage()), didFinishPicking: {}, aspectRatioPreset: .presetSquare) // Specify aspectRatioPreset here
    }
}

下面是调用 ImagePicker 的结构:

struct ImageView: View {
    @State private var showingImagePicker = false
    @State private var linkImage: UIImage? = UIImage(named: "defaultLinkImage")
    @State private var linkImageURL: String = ""
    @State private var previousLinkImageURL: String = ""
    @State private var showAlert = false
    let defaultLinkImage = UIImage(named: "defaultLinkImage")
    let selectedDate: String
    let selectedGender: String
    let selectedPreference: String

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Color.black
                    .ignoresSafeArea()

                VStack {
                    Text("Add a great picture of you.")
                        .font(.custom("Futura-Bold", size: geometry.size.width * 0.1))
                        .foregroundColor(.white)
                        .multilineTextAlignment(.center)
                        .lineLimit(2)
                        .minimumScaleFactor(0.5)
                        .layoutPriority(1)
                        .padding()

                    Button(action: {
                        HapticManager.shared.triggerHapticFeedback()
                        self.showingImagePicker = true
                    }) {
                        Image(uiImage: linkImage!)
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: 300, height: 400)
                            .clipShape(RoundedRectangle(cornerRadius: 25))
                            .overlay(RoundedRectangle(cornerRadius: 25).stroke(Color.white, lineWidth: 4))
                            .shadow(color: .gray, radius: 10, x: 5, y: 5)
                            .shadow(color: .white, radius: 10, x: -5, y: -5)
                            .padding(.bottom, 20)
                    }

                    if linkImage == defaultLinkImage {
                        Button(action: {
                            HapticManager.shared.triggerHapticFeedback()
                            self.showAlert = true
                        }) {
                            Text("Continue.")
                                .font(.custom("Futura-Bold", size: 20))
                                .padding()
                                .background(Color.gray)
                                .foregroundColor(.white)
                                .clipShape(Capsule())
                                .alert(isPresented: $showAlert) {
                                    Alert(title: Text("Error"), message: Text("Please choose a picture before proceeding."), dismissButton: .default(Text("Gotcha.")))
                                }
                        }
                        .padding()
                    } else {
                        NavigationLink(destination: CompleteView(date: self.selectedDate, gender: self.selectedGender, preferences: selectedPreference, imageURL: URL(string: linkImageURL))) {
                            Text("Continue.")
                                .font(.custom("Futura-Bold", size: 20))
                                .padding()
                                .background(Color.accentColor)
                                .foregroundColor(.white)
                                .clipShape(Capsule())
                        }
                        .padding()
                        .onTapGesture {
                            uploadData()
                        }
                    }
                }
            }
        }
        .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
            ImagePicker(image: self.$linkImage, didFinishPicking: {
                self.showingImagePicker = false
            }, aspectRatioPreset: .preset4x3)
        }
        .navigationBarBackButtonHidden(true)
    }

    func loadImage() {
        guard let inputImage = linkImage else { return }
        
        // Delete previous profile picture if exists
        if !previousLinkImageURL.isEmpty {
            deletePreviousLinkPicture(previousURL: previousLinkImageURL)
        }
        
        // Create a unique file name
        let fileName = UUID().uuidString
        let folderPath = "Link_Pictures/"
        let storageRef = Storage.storage().reference().child(folderPath + fileName)
        guard let imageData = inputImage.jpegData(compressionQuality: 0.5) else { return }
        
        // Metadata
        let metadata = StorageMetadata()
        metadata.contentType = "image/jpeg"
        
        // Upload the file to Firebase Storage
        storageRef.putData(imageData, metadata: metadata) { _, error in
            if let error = error {
                print("Error uploading: \(error.localizedDescription)")
                return
            }
            
            // After the file is uploaded, get the download URL.
            storageRef.downloadURL { (url, error) in
                guard let downloadURL = url else {
                    print("An error occurred while getting the download URL")
                    return
                }
                
                self.linkImageURL = downloadURL.absoluteString
                self.previousLinkImageURL = downloadURL.absoluteString // Store the current profile image URL as the previous URL
                
                // Call the uploadData method after getting the image URL
                uploadData()
            }
        }
    }

    func deletePreviousLinkPicture(previousURL: String) {
        // Get the storage reference from the previous URL
        let storageRef = Storage.storage().reference(forURL: previousURL)
        
        // Delete the file from Firebase Storage
        storageRef.delete { error in
            if let error = error {
                print("Error deleting picture: \(error.localizedDescription)")
            } else {
                print("Previous picture deleted successfully")
            }
        }
    }
    
    func uploadData() {
        // Call the LinkRegLogic uploadData method
        LinkRegLogic.uploadData(date: self.selectedDate, gender: self.selectedGender, preferences: self.selectedPreference, imageURL: linkImageURL)
    }
}

非常感谢您的帮助,我是 SwiftUI 的初学者,我真的很感激。

我尝试在 ChatGPT 的帮助下在 ImagePicker 中重写我的函数,但我一无所获。

此外,如果您可以使用 TOCropViewController 以外的其他图像拾取和裁剪框架来完成这项工作,同时确保我可以在调用 ImagePicker 时将裁剪叠加的大小更改为参数,那也很棒,我不是特别依恋 TOCropViewController。

P.S. 我还使用 ImagePicker 在不是工作表的视图中挑选图像。

swiftui swift3 uiimagepickercontroller imagepicker image-cropper

评论


答: 暂无答案