这是在 SwiftUI 中使用 PHPickerViewController 的正确方法吗?因为我漏了很多

问题描述 投票:0回答:1

我正在尝试确定是否是我的代码导致了问题,或者我是否应该向 Apple 提交错误报告。

在一个新项目中,我有以下代码:

内容视图()

import SwiftUI

struct ContentView: View {
    
    @State private var showingImagePicker = false
    @State private var inputImage: UIImage?
    @State private var image: Image?
    
    var body: some View {
        ZStack {
            Rectangle()
                .fill(Color.secondary)
            if image != nil {
                image?
                    .resizable()
                    .scaledToFit()
            } else {
                Text("Tap to select a picture")
                    .foregroundColor(.white)
                    .font(.headline)
            }
        }
        .onTapGesture {
            self.showingImagePicker = true
        }
        
        .sheet(isPresented: $showingImagePicker, onDismiss: loadImage){
            SystemImagePicker(image: self.$inputImage)
            
        }
    }
    func loadImage() {
        guard let inputImage = inputImage else { return }
        image = Image(uiImage: inputImage)
        
    }
}

系统图像选择器.swift

import PhotosUI
import SwiftUI

struct SystemImagePicker: UIViewControllerRepresentable {
    
    @Environment(\.presentationMode) private var presentationMode
    
    @Binding var image: UIImage?
    
    func makeUIViewController(context: Context) -> PHPickerViewController {
        var configuration = PHPickerConfiguration()
        configuration.selectionLimit = 1
        configuration.filter = .images
        
        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = context.coordinator
        return picker
    }
    
    func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
        
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self)
    }
    
    class Coordinator: NSObject, PHPickerViewControllerDelegate {
        let parent: SystemImagePicker
        
        init(parent: SystemImagePicker) {
            self.parent = parent
        }
        
        func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            for img in results {
                guard img.itemProvider.canLoadObject(ofClass: UIImage.self) else { return }
                img.itemProvider.loadObject(ofClass: UIImage.self) { image, error in
                    if let error = error {
                        print(error)
                        return
                    }
                    
                    guard let image = image as? UIImage else { return }
                    self.parent.image = image
                    self.parent.presentationMode.wrappedValue.dismiss()
                }
            }
        }
    }
}

但是,当仅选择一个图像时(根据我的代码,不选择然后“改变主意”并选择另一个不同的图像),在 Xcode 中运行内存图时会出现这些泄漏。

这是我的代码,还是Apple上的代码?

无论如何,图像选择器上的

Cancel
按钮也不起作用。因此,用户不能只是关闭选择器表,必须选择图像来关闭该表。

关于旧 UIImagePickerController 的进一步说明

以前,我曾将此代码用于旧的

UIImagePickerController

import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {

    @Environment(\.presentationMode) var presentationMode
    @Binding var image: UIImage?
    
    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        let parent: ImagePicker
        
        init(_ parent: ImagePicker) {
           self.parent = parent
        }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let uiImage = info[.originalImage] as? UIImage {
                parent.image = uiImage
            }
            parent.presentationMode.wrappedValue.dismiss()
        }
        
        deinit {
            print("deinit")
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
        
    }
}

这也会导致选择图像时发生泄漏,但数量要少得多:

swiftui memory-leaks uiimagepickercontroller phpickerviewcontroller
1个回答
1
投票

我知道你问这个问题已经一年多了,但希望这对你或其他寻找答案的人有帮助。

我在帮助文件中使用了这段代码:

import SwiftUI
import PhotosUI

struct ImagePicker: UIViewControllerRepresentable {

let configuration: PHPickerConfiguration
@Binding var selectedImage: UIImage?
@Binding var showImagePicker: Bool

func makeCoordinator() -> Coordinator {
    
    Coordinator(self)
}

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

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

extension ImagePicker {

class Coordinator: NSObject, PHPickerViewControllerDelegate {
    
    private let parent: ImagePicker
    
    init(_ parent: ImagePicker) {
        
        self.parent = parent
    }
    
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        
        picker.dismiss(animated: true) {
            
            self.parent.showImagePicker = false
        }
        
        guard let provider = results.first?.itemProvider else { return }
        
        if provider.canLoadObject(ofClass: UIImage.self) {
            
            provider.loadObject(ofClass: UIImage.self) { image, _ in
                
                self.parent.selectedImage = image as? UIImage
            }
        }
        
        parent.showImagePicker = false
    }
}
}    

这在您的视图中(我在此处设置了配置,以便我可以根据我使用选择器的用途传递自定义版本,提供了 2 个):

@State private var showImagePicker = false
@State private var selectedImage: UIImage?
@State private var profileImage: Image?

var profileConfig: PHPickerConfiguration {
    
    var config = PHPickerConfiguration()
    config.filter = .images
    config.selectionLimit = 1
    config.preferredAssetRepresentationMode = .current
    return config
}

var mediaConfig: PHPickerConfiguration {
    
    var config = PHPickerConfiguration()
    config.filter = .any(of: [.images, .videos])
    config.selectionLimit = 1
    config.preferredAssetRepresentationMode = .current
    return config
}

这会进入你的身体。你可以按照你想要的方式定制它,但这就是我所拥有的,所以我不想尝试将它拼凑出来:

                HStack {
                    
                    Button {
                        
                        showImagePicker.toggle()
                    } label: {
                        
                        Text("Select Photo")
                            .foregroundColor(Color("AccentColor"))
                    }
                    .sheet(isPresented: $showImagePicker) {
                        
                        loadImage()
                    } content: {
                        
                        ImagePicker(configuration: profileConfig, selectedImage: $selectedImage, showImagePicker: $showImagePicker)
                            
                    }
                }
                
                if profileImage != nil {
                    
                    profileImage?
                        .resizable()
                        .scaledToFill()
                        .frame(width: 100, height: 100)
                        .clipShape(Circle())
                        .shadow(radius: 5)
                        .overlay(Circle().stroke(Color.black, lineWidth: 2))
                }
                else {
                    
                    Image(systemName: "person.crop.circle")
                        .resizable()
                        .foregroundColor(Color("AccentColor"))
                        .frame(width: 100, height: 100)
                }

我还会为您提供加载图像的功能(我将重新放大:

func loadImage() {
    
    guard let selectedImage = selectedImage else { return }
    profileImage = Image(uiImage: selectedImage)        
}

我还在我的表单上使用它来更新图像(如果图像发生更改),但您可以将它用于您用于身体的任何内容(列表、表单等。无论需要 .onChange):

.onChange(of: selectedImage) { _ in
            
            loadImage()
        }

我注意到在很多教程中几乎没有提到这一行,这就是取消按钮功能的原因(我不知道关闭是否是必要的,但我添加了它并且它起作用了,所以我把它留在了示例中):

picker.dismiss(animated: true)

我希望我添加的所有内容都能对您有所帮助。它似乎没有泄漏任何内容,并且让您可以使用取消按钮。

祝你好运!

© www.soinside.com 2019 - 2024. All rights reserved.