我希望从 Swift UI 管理的按钮调用 UIKit UIViewController 内的函数
在我的 Swift UI 视图中,我有:
struct CameraView: View {
var body: some View {
cameraViewController = CameraViewController()
...
我看到它创建了两个实例,一个像调用任何类一样直接创建,另一个由 Swift UI 管理 UIKit UIViewControllers 所需的
makeUIViewController
方法创建。
但是,当我将函数附加到 Swift UI 中的按钮时,
cameraViewController.takePhoto()
引用的实例不是显示的实例。
如何获取显示的具体实例?
这个问题可能有多种解决方案,但无论如何,您需要找到一种方法来保留对
UIViewController
的引用或与其进行通信。因为 SwiftUI 视图本身非常短暂,所以您不能只在视图本身中存储引用,因为它可以随时重新创建。
使用工具:
ObservableObject——这将允许您将数据存储在类而不是结构中,并且可以更轻松地存储引用、连接数据等
协调器 - 在
UIViewRepresentable
中,您可以使用协调器模式,该模式允许您存储对 UIViewController
的引用并与其通信
组合发布者——这些是完全可选的,但我选择在这里使用它们,因为它们是一种无需太多样板代码即可移动数据的简单方法。
import SwiftUI
import Combine
struct ContentView: View {
@StateObject var vcLink = VCLink()
var body: some View {
VStack {
VCRepresented(vcLink: vcLink)
Button("Take photo") {
vcLink.takePhoto()
}
}
}
}
enum LinkAction {
case takePhoto
}
class VCLink : ObservableObject {
@Published var action : LinkAction?
func takePhoto() {
action = .takePhoto
}
}
class CustomVC : UIViewController {
func action(_ action : LinkAction) {
print("\(action)")
}
}
struct VCRepresented : UIViewControllerRepresentable {
var vcLink : VCLink
class Coordinator {
var vcLink : VCLink? {
didSet {
cancelable = vcLink?.$action.sink(receiveValue: { (action) in
guard let action = action else {
return
}
self.viewController?.action(action)
})
}
}
var viewController : CustomVC?
private var cancelable : AnyCancellable?
}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
func makeUIViewController(context: Context) -> CustomVC {
return CustomVC()
}
func updateUIViewController(_ uiViewController: CustomVC, context: Context) {
context.coordinator.viewController = uiViewController
context.coordinator.vcLink = vcLink
}
}
这里发生了什么:
VCLink
是一个 ObservableObject
,我用它作为视图之间沟通的中间人ContentView
引用了 VCLink
- 当按下按钮时,Publisher
上的 VCLink
会将其传达给任何订阅者VCRepresented
时,我会在其 VCLink
中存储对 ViewController 和
Coordinator
Coordinator
获取 Publisher
并在其 sink
方法中对存储的 ViewController
执行操作。在这个演示中,我只是打印动作。在您的示例中,您想要触发照片本身。如果您需要直接调用视图控制器的函数并且不需要不必要的代码,可以从 SwiftUI 引用您的视图控制器:
class Reference<T: AnyObject> {
weak var object: T?
}
class PlayerViewController: UIViewController {
func resume() {
print("resume")
}
func pause() {
print("pause")
}
}
struct PlayerView: UIViewControllerRepresentable {
let reference: Reference<PlayerViewController>
func makeUIViewController(context: Context) -> PlayerViewController {
let controller = PlayerViewController()
// Set controller to the reference
reference.object = controller
return controller
}
func updateUIViewController(_ viewController: PlayerViewController, context: Context) {
}
}
struct ContentView: View {
let reference = Reference<PlayerViewController>()
var body: some View {
Button("Resume") {
reference.object?.resume()
}
Button("Pause") {
reference.object?.pause()
}
PlayerView(reference: reference)
}
}