我正在构建一个相机应用程序,其所有 UI 都在 SwiftUI(父级)中,其中包含一个包含所有录制功能的 UIKit Controller 。用户界面非常复杂,因此希望尽可能保留项目的这种结构。
UIKit 类有一些函数,例如 startRecord() stopRecord(),我希望从 SwiftUI View 触发这些函数。因此,我想从 SwiftUI View “调用”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 Controller ...
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 View 。这样,我就可以命令式地调用它们。我没有尝试解析您的代码,而是做了一个这个概念的小例子:
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
有一个 PassthroughSubject
,可以从 UIKit View (或在本例中为 UIViewController)订阅。它属于 ContentView
,并通过参数传递给 VCRepresented
。
在 VCRepresented
中,Coordinator
上有一个方法用于订阅发布者 (messagePassthrough
) 并对消息进行操作。您可以通过枚举 (MessageBridgeMessage
) 上的关联属性传递参数。如果需要,返回值可以存储在 MessageBridge
上的 @Published 属性中(或者,您可以设置另一个发布者以相反的方向)。
它有点冗长,但似乎是一个非常可靠的模式,可以与您需要的树的任何级别(SwiftUI View 、可表示 View 、UIKit View 等)进行通信。
关于swift - 通过 UIViewControllerRepresentable 实现复杂的 UIKit + SwiftUI 界面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66619751/