简体   繁体   English

从 SwiftUI 视图调用本地 UIViewController function

[英]Calling a local UIViewController function from a SwiftUI View

I'm trying to call a local ViewController function from ContentView.我正在尝试从 ContentView 调用本地 ViewController function。 The function uses some local variables and cannot be moved outside the ViewController. function 使用了一些局部变量,不能移到 ViewController 之外。

class ViewController: UIViewController {
    func doSomething() {...}
} 

extension ViewController : LinkViewDelegate {...}

located on a different file:位于不同的文件中:

struct ContentView: View {

    init() {
        viewController = .init(nibName:nil, bundle:nil)
    }
    var viewController: viewController

var body: some View {
    Button(action: {self.viewController.doSomething()}) {
            Text("Link Account")
        }
    }
}

UIViewController cannot be changed to something like UIViewRepresentable because LinkViewDelegate can only extend UIViewController. UIViewController 不能更改为 UIViewRepresentable 之类的东西,因为 LinkViewDelegate 只能扩展 UIViewController。

So you need to create a simple bool binding in SwiftUI, flip it to true to trigger the function call in the UIKit viewController, and then set it back to false until the next time the swiftUI button is pressed. So you need to create a simple bool binding in SwiftUI, flip it to true to trigger the function call in the UIKit viewController, and then set it back to false until the next time the swiftUI button is pressed. (As for LinkViewDelegate preventing something like UIViewControllerRepresentable that shouldn't stop you, use a Coordinator to handle the delegate calls.) (至于LinkViewDelegate防止像UIViewControllerRepresentable这样不应该阻止你的东西,使用Coordinator来处理委托调用。)

struct ContentView: View {

    @State var willCallFunc = false

    var body: some View {
        ViewControllerView(isCallingFunc: $willCallFunc)

        Button("buttonTitle") {
            self.willCallFunc = true
        }
    }
}

struct ViewControllerView: UIViewControllerRepresentable {

    @Binding var isCallingFunc: Bool

    func makeUIViewController(context: Context) -> YourViewController {
        makeViewController(context: context) //instantiate vc etc.
    }

    func updateUIViewController(_ uiViewController: YourViewController, context: Context) {
        if isCallingFunc {
            uiViewController.doSomething()
            isCallingFunc = false
        }
    }
}

You could pass the instance of ViewController as a parameter to ContentView:您可以将 ViewController 的实例作为参数传递给 ContentView:

struct ContentView: View {
    var viewController: ViewController // first v lowercase, second one Uppercase

    var body: some View {
        Button(action: { viewController.doSomething() }) { // Lowercase viewController
            Text("Link Account")
        }
    }

    init() {
        self.viewController = .init(nibName:nil, bundle:nil) // Lowercase viewController
    }
} 

// Use it for the UIHostingController in SceneDelegate.swift
window.rootViewController = UIHostingController(rootView: ContentView()) // Uppercase ContentView

Updated answer to better fit the question.更新了答案以更好地适应问题。

Here is a way that I've come up with which doesn't result in the "Modifying state during view update, this will cause undefined behavior" problem.这是我想出的一种方法,它不会导致“在视图更新期间修改 state,这将导致未定义的行为”问题。 The trick is to pass a reference of your ViewModel into the ViewController itself and then reset the boolean that calls your function there, not in your UIViewControllerRepresentable.诀窍是将 ViewModel 的引用传递给 ViewController 本身,然后重置调用 function 的 boolean ,而不是在 UIViewControllerRepresentable 中。

public class MyViewModel: ObservableObject {
    @Published public var doSomething: Bool = false
}


struct ContentView: View {

    @StateObject var viewModel = MyViewModel()

    var body: some View {
        MyView(viewModel: viewModel)

        Button("Do Something") {
            viewModel.doSomething = true
        }
    }
}


struct MyView: UIViewControllerRepresentable {
    
    @ObservedObject var viewModel: MyViewModel
    
    func makeUIViewController(context: Context) -> MyViewController {
        return MyViewController(viewModel)
    }
    
    func updateUIViewController(_ viewController: MyViewController, context: Context) {
        if viewModel.doSomething {
            viewController.doSomething()
            // boolean will be reset in viewController
        }
    }
}


class MyViewController: UIViewController {
    
    var viewModel: MyViewModel
    
    public init(_ viewModel: MyViewModel) {
        self.viewModel = viewModel
    }
    
    public func doSomething() {
        // do something, then reset the flag
        viewModel.doSomething = false
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM