首页 > 解决方案 > 如何在包装器 Swift View 中使用自定义 UIView 的扩展功能?

问题描述

(在 Xcode 11.3、Swift 5.1.3 中测试)

我想扩展 UIView,用 UIViewRepresentable 包装它,并将其用作 Swift View。但是,我很难从包装器 Swift View 访问自定义 UIView 的扩展功能。

class UICameraCaptureImageView: UIImageView, AVCaptureVideoDataOutputSampleBufferDelegate {
  @State var capturedImage: UIImage? = UIImage(named: "default_placeholder")

  func startCameraCapture()  {  
    // start camera capture when it is ready
  }

  // AVCaptureVideoDataOutputSampleBufferDelegate delegate method follows
  // ...

}
struct CameraCaptureImageView: UIViewRepresentable {

  // cannot set containedUIView in makeUIView/updateUIView, and they are not mutating method
  private var containedUIView: UICameraCaptureImageView?

  func makeUIView(context: UIViewRepresentableContext<CameraCaptureImageView>) -> 
      UICapturedImageView {
    UICapturedImageView()     
  }

  func updateUIView(_ uiView: UICapturedImageView, 
      context: UIViewRepresentableContext< CameraCaptureImageView>) {
    uiView.image = capturedImage
  }

  func startCameraCapture()  {  
    // redirect to UICameraCaptureImageView.startCameraCapture(), 
    // but cannot set self.containedUIView
    guard self.containedUIView != nil else {
      print("The containedUICaptureView doesn't exist")
      return
    }
    self.containedUIView?.startCameraCapture()
  }
}

起初,虽然是一种有状态的策略,但我尝试在 CameraCaptureImageView 中声明一个成员变量,并在创建时设置 UICameraCaptureImageView 实例。但正如您所见,makeUIView() 并未声明为变异方法,因此我无法变异 CameraCaptureImageView 的任何成员。

如何从 UIViewRepresentable 包装器访问我的 UIView 子类中的扩展自定义函数 startCameraCapture()?或者,是否有任何无状态、体面的解决方案可以在 SwiftUI 中使用扩展的旧 UIView?

标签: iosswiftuikitswiftuiuiviewrepresentable

解决方案


你应该创建一个Coordinator为你管理这个穿梭的。它是一个class因此并不严格受制于非变异语义。

struct CameraCaptureImageView: UIViewRepresentable {

  func makeUIView(context: UIViewRepresentableContext<CameraCaptureImageView>) -> 
      UICapturedImageView {
    return UICapturedImageView()     
  }

  func makeCoordinator() -> Coordinator {
    return .init(self)
  }
}

extension CameraCaptureImageView {

  // add any delegate/protocol conformances here, it's just an object!
  private class Coordinator  {
    let cameraCaptureImageView: CameraCaptureImageView

    init(_ cameraCaptureImageView: CameraCaptureImageView) {
       // CameraCaptureImageView is a `struct`, so it's a copy!
       self.cameraCaptureImageView = cameraCaptureImageView
    }

    // now here is all your UIView specific logic
  }
}

需要发出信号吗?添加一个闭包View,您Coordinator可以在某些事件上调用它。

struct CameraCaptureImageView: ... {

    let onSomeEvent: (Event) -> Void
}

class Coordinator {

    let cameraCaptureImageView: ...

    func view(_ view: UIViewOfSomeKind, didReceive event: Event) {
      cameraCaptureImageView.onEvent(event)
    }
}

推荐阅读