通过 UIViewControllerRepresentable 实现复杂的 UIKit + SwiftUI 界面

2024-02-05

我正在构建一个相机应用程序,其中所有 UI 都在 SwiftUI(父级)中,其中包含一个包含所有录制功能的 UIKit 控制器。用户界面非常复杂,因此如果可能的话,希望项目保留这种结构。

UIKit 类有一些函数,例如 startRecord() stopRecord(),我希望从 SwiftUI 视图触发它们。因此,我想从 SwiftUI 视图“调用”UIKit 函数。

我正在尝试 UIViewControllerRepresentable,能够对全局变量更改执行更新,但我仍然无法从 SwiftUI 父级调用我想要触发的各个函数。

这是 SwiftUI 文件:

init(metalView: MetalViewController?) {
    self.metalView = MetalViewController(appStatus: appStatus)
}

var body: some View {
    
    ZStack {
        
        // - Camera view
        metalView
            .edgesIgnoringSafeArea(.top)
            .padding(.bottom, 54)
        
        VStack {
            
            
            LateralMenuView(appStatus: appStatus, filterTooltipShowing: $_filterTooltipShowing)
            
            Button("RECORD", action: {
                print("record button pressed")
                metalView?.myMetalDelegate.switchRecording(). // <-- Not sure about this
            })

这是 MetalViewController:

protocol MetalViewControllerDelegate {
    func switchRecording()
}

// MARK: - The secret sauce for loading the MetalView (UIKit -> SwiftUI)
struct MetalViewController: UIViewControllerRepresentable {

var appStatus: AppStatus
typealias UIViewControllerType = MetalController
var myMetalDelegate: MetalViewControllerDelegate!

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

func makeUIViewController(context: UIViewControllerRepresentableContext<MetalViewController>) -> MetalController {
    let controller = MetalController(appStatus: appStatus)
    return controller
}

func updateUIViewController(_ controller: MetalController, context: UIViewControllerRepresentableContext<MetalViewController>) {
    controller.changeFilter()
}

class Coordinator: NSObject, MetalViewControllerDelegate {
    var controller: MetalViewController

    init(metalViewController: MetalViewController) {
        controller = metalViewController
    }
    func switchRecording() {
        print("just testing")
    }
}

}

和 UIKit 控制器...

class MetalController: UIViewController {

var _mydelegate: MetalViewControllerDelegate?
...
override func viewDidLoad() {
 ...
    self._mydelegate = self
}

extension MetalController: MetalViewControllerDelegate {
    func switchRecording() {
        print("THIS SHOULD BE WORKING, BUT ITS NOT")
    }
}

我喜欢使用组合通过 ObservableObject 将消息传递到 UIKit 视图。这样,我就可以命令式地调用它们。我没有尝试解析您的代码,而是做了一个这个概念的小例子:

import SwiftUI
import Combine

enum MessageBridgeMessage {
    case myMessage(parameter: Int)
}

class MessageBridge : ObservableObject {
    @Published var result = 0
    
    var messagePassthrough = PassthroughSubject<MessageBridgeMessage, Never>()
}

struct ContentView : View {
    @StateObject private var messageBridge = MessageBridge()
    
    var body: some View {
        VStack {
            Text("Result: \(messageBridge.result)")
            Button("Add 2") {
                messageBridge.messagePassthrough.send(.myMessage(parameter: messageBridge.result))
            }
            VCRepresented(messageBridge: messageBridge)
        }
    }
}

struct VCRepresented : UIViewControllerRepresentable {
    var messageBridge : MessageBridge
    
    func makeUIViewController(context: Context) -> CustomVC {
        let vc = CustomVC()
        context.coordinator.connect(vc: vc, bridge: messageBridge)
        return vc
    }
    
    func updateUIViewController(_ uiViewController: CustomVC, context: Context) {
        
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }
    
    class Coordinator {
        private var cancellable : AnyCancellable?
        
        func connect(vc: CustomVC, bridge: MessageBridge) {
            cancellable = bridge.messagePassthrough.sink(receiveValue: { (message) in
                switch message {
                case .myMessage(let parameter):
                    bridge.result = vc.addTwo(input: parameter)
                }
            })
        }
    }
}

class CustomVC : UIViewController {
    func addTwo(input: Int) -> Int {
        return input + 2
    }
}

在示例中,MessageBridge has a PassthroughSubject可以从 UIKit 视图(或者在本例中为 UIViewController)订阅。它的所有者是ContentView并通过参数传递给VCRepresented.

In VCRepresented,有一个方法Coordinator订阅发布者 (messagePassthrough)并根据消息采取行动。您可以通过枚举上的关联属性传递参数(MessageBridgeMessage)。返回值可以存储在 @Published 属性上MessageBridge如果您需要它们(或者,您可以设置另一个发布商来走相反的方向)。

它有点冗长,但似乎是一个非常可靠的模式,可以与您需要的树的任何级别(SwiftUI 视图、可表示视图、UIKit 视图等)进行通信。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过 UIViewControllerRepresentable 实现复杂的 UIKit + SwiftUI 界面 的相关文章

随机推荐