(tested in Xcode 11.3, Swift 5.1.3)
I want to extend UIView, wrap it with UIViewRepresentable, and use it as a Swift View. However, it is difficult for me to access extended functions of the custom UIView from the wrapper Swift View.
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()
}
}
At first, though it's a kind of stateful strategy, I tried to declare a member variable in CameraCaptureImageView and set the UICameraCaptureImageView instance when it is made. But as you see, makeUIView() is not declared as mutating method so that I cannot mutate any members of CameraCaptureImageView.
How can I access the extended custom function startCameraCapture() in my UIView subclass from the UIViewRepresentable wrapper? Or, is there any stateless, decent solution to use extended old UIView in SwiftUI?
You are supposed to create a Coordinator
that manages this shuttling for you. It's a class
and therefore is not strictly beholden to non-mutating semantics.
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
}
}
Need to signal out? Add a closure on your View
that your Coordinator
can call on certain events.
struct CameraCaptureImageView: ... {
let onSomeEvent: (Event) -> Void
}
class Coordinator {
let cameraCaptureImageView: ...
func view(_ view: UIViewOfSomeKind, didReceive event: Event) {
cameraCaptureImageView.onEvent(event)
}
}