在 SwiftUI 中从 UIViewController 调用函数

问题描述 投票:0回答:2

我希望从 Swift UI 管理的按钮调用 UIKit UIViewController 内的函数

在我的 Swift UI 视图中,我有:

struct CameraView: View {
        var body: some View {
            cameraViewController = CameraViewController()
...

我看到它创建了两个实例,一个像调用任何类一样直接创建,另一个由 Swift UI 管理 UIKit UIViewControllers 所需的

makeUIViewController
方法创建。

但是,当我将函数附加到 Swift UI 中的按钮时,

cameraViewController.takePhoto()
引用的实例不是显示的实例。

如何获取显示的具体实例?

swiftui uikit
2个回答
8
投票

这个问题可能有多种解决方案,但无论如何,您需要找到一种方法来保留对

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
    }
}

这里发生了什么:

  1. VCLink
    是一个
    ObservableObject
    ,我用它作为视图之间沟通的中间人
  2. ContentView
    引用了
    VCLink
    - 当按下按钮时,
    Publisher
    上的
    VCLink
    会将其传达给任何订阅者
  3. 当创建/更新
    VCRepresented
    时,我会在其
    VCLink
     中存储对 ViewController 和 
    Coordinator
  4. 的引用
  5. Coordinator
    获取
    Publisher
    并在其
    sink
    方法中对存储的
    ViewController
    执行操作。在这个演示中,我只是打印动作。在您的示例中,您想要触发照片本身。

3
投票

如果您需要直接调用视图控制器的函数并且不需要不必要的代码,可以从 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)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.