首页 > 解决方案 > iOS14 中的 UIViewControllerRepresentable 和属性包装器行为,与 CropViewController 一起使用

问题描述

我正在使用“UIImagePickerController”和“CropViewController”来修改从照片库中选择的图像。由于目标 OS 版本为 13+,“UIViewControllerRepresentable”用于处理 SwiftUI 中的这些 UIKit 部分。Xcode 版本为 12.0。

(注)CropViewController 是一个第三方库。来源在这里:https ://github.com/TimOliver/TOCropViewController

我在 iOS14 上启动时发现了一个奇怪的行为,无法找出原因。请帮我!!

以下是我所做的。我在编号从“//**** 1****”到“//**** 3****”的代码中添加了“print()”,以向您展示什么是奇怪的。

[ UIImagePickerController ]

import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {
    
    @Binding var isPresented: Bool       // this is a flag used for presenting "UIImagePickerController"
    @Binding var materialImage: UIImage? // this is to be passed to "CropViewController"
    @Binding var showCropView: Bool      // this is a flag used for presenting "CropViewController"

    func makeCoordinator() -> ImagePicker.Coordinator {
        return ImagePicker.Coordinator(
            isPresented: $isPresented,
            materialImage: $materialImage,
            showCropView: $showCropView
        )
    }
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.sourceType = .photoLibrary
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController,
                                context: UIViewControllerRepresentableContext<ImagePicker>) {}
    
}

extension ImagePicker {
    class Coordinator: NSObject, UINavigationControllerDelegate {
        @Binding var isPresented: Bool
        @Binding var materialImage: UIImage?
        @Binding var showCropView: Bool
        public init(isPresented: Binding<Bool>, materialImage: Binding<UIImage?>, showCropView: Binding<Bool>) {
            self._isPresented = isPresented
            self._materialImage = materialImage
            self._showCropView = showCropView
        }
    }
}

extension ImagePicker.Coordinator: UIImagePickerControllerDelegate {
    public func imagePickerController(
        _ picker: UIImagePickerController,
        didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        DispatchQueue.mainSyncSafe {
            self.materialImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
            isPresented = false  // close UIImagePicker
            showCropView = true  // launch CropViewController
            print("materialImage @ImagePicker: \(materialImage)")  // ****1****
        }
    }
    public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        isPresented = false
    }
}

[作物视图控制器]

import SwiftUI
import CropViewController

struct CropView: UIViewControllerRepresentable {
    
    var materialImage: UIImage?       // selected image with ImagePickerController
    @Binding var newImage: UIImage?   // modified image with CropViewController
    @Binding var isPresented: Bool    // this is a flag to show/hide CropViewController
    
    init(materialImage: UIImage?, newImage: Binding<UIImage?>, isPresented: Binding<Bool>){
        self.materialImage = materialImage
        self._newImage = newImage
        self._isPresented = isPresented
    }

    func makeCoordinator() -> CropView.Coordinator {
        return CropView.Coordinator(
            isPresented: $isPresented,
            newImage: $newImage
        )
    }
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<CropView>) -> CropViewController {
        let cropViewController = CropViewController(image: materialImage ?? UIImage())
        print("materialImage @CropView: \(materialImage)")  // ****2****
        cropViewController.delegate = context.coordinator
        return cropViewController
    }
    
    func updateUIViewController(_ uiViewController: CropViewController,
                                context: UIViewControllerRepresentableContext<CropView>) {}
    
}

extension CropView {
    class Coordinator: NSObject {
        @Binding var isPresented: Bool
        @Binding var newImage: UIImage?
        public init(isPresented: Binding<Bool>, newImage: Binding<UIImage?>) {
            self._isPresented = isPresented
            self._newImage = newImage
        }
    }
}

extension CropView.Coordinator: ObservableObject, CropViewControllerDelegate {
    
    func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
        newImage = image
        isPresented = false
        NotificationCenter.default.post(name: .imageSelected, object: nil, userInfo: nil)
    }
    
    func cropViewController(_ cropViewController: CropViewController, didFinishCancelled cancelled: Bool) {
        isPresented = false
    }
    
}

[内容.swift]

import SwiftUI

struct Content: View {
    
    @State var showImagePicker: Bool = false    // flag for show/hide UIImagePickerController
    @State var showCropView: Bool = false       // flag for show/hide CropViewController
    @State var materialImage: UIImage?          // selected image with UIImagePickerController
    @State var newImage: UIImage?               // modified Image with CropViewController
    
    var body: some View {     
       VStack{
          // show some views

          Spacer().frame(height: 0).sheet(isPresented: $showImagePicker) {
              ImagePicker(isPresented: self.$showImagePicker, materialImage: self.$materialImage, showCropView: self.$showCropView)
          }

          Spacer().frame(height: 0.0).sheet(isPresented: self.$showCropView) {
              CropView(materialImage: self.materialImage, newImage: self.$newImage, isPresented: self.$showCropView)
          }

       }
       .onReceive( NotificationCenter.default.publisher(for: .imageSelected) ){_ in
           print("materialImage @Content: \(materialImage)")  // ****3****
       }
    }
}

使用 iOS13.5 的模拟器启动时,调试打印如下。有一条未知消息,但这很好。

materialImage @ImagePicker: Optional(<UIImage:0x600002810510 anonymous {4032, 3024}>) // ****1****
materialImage @CropView: Optional(<UIImage:0x600002810510 anonymous {4032, 3024}>) // ****2****
2020-09-22 sbx[ ] [Common] _BSMachError: port c603; (os/kern) invalid capability (0x14) "Unable to insert COPY_SEND"
materialImage @CreateAdv_SwiftUIView: Optional(<UIImage:0x600002810510 anonymous {4032, 3024}>)  // ****3****

使用iOS14的模拟器,CropView屏幕是黑色的,调试打印如下。“materialImage @CropView”为零。更奇怪的是,在关闭此裁剪视图屏幕(使用取消按钮)并再次选择图像后,一切正常(我可以选择和修改图像)。

iOS14有bug吗?还是我的实施不好?

materialImage @ImagePicker: Optional(<UIImage:0x600003f3cf30 anonymous {4032, 3024}>) // ****1****
materialImage @CropView: nil // ****2****
2020-09-22 sbx[ ] [ScrollView] UIScrollView is ignoring an attempt to set zoomScale to a non-finite value: inf
2020-09-22 sbx[ ] [Unknown process name] CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
materialImage @CreateAdv_SwiftUIView: Optional(<UIImage:0x600003f3cf30 anonymous {4032, 3024}>) // ****3****
2020-09-22 sbx[ ] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed

我希望你的建议!谢谢!!

标签: iosswiftswiftuiios14

解决方案


推荐阅读