从 SwiftUI 视图调用本地 UIViewController 函数

Liv*_*Liv 6 view ios swift swiftui

我正在尝试从 ContentView 调用本地 ViewController 函数。该函数使用了一些局部变量,不能移动到 ViewController 之外。

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

extension ViewController : LinkViewDelegate {...}
Run Code Online (Sandbox Code Playgroud)

位于不同的文件中:

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")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

UIViewController 不能更改为 UIViewRepresentable 之类的东西,因为 LinkViewDelegate 只能扩展 UIViewController。

Mad*_*las 8

因此,您需要在 SwiftUI 中创建一个简单的 bool 绑定,将其翻转为 true 以触发 UIKit viewController 中的函数调用,然后将其设置回 false,直到下次按下 swiftUI 按钮。(至于LinkViewDelegate防止类似的事情UIViewControllerRepresentable不应该阻止您,请使用 aCoordinator来处理委托调用。)

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我收到“在视图更新期间修改状态,这将导致未定义的行为。” 设置 isCallingFunc = false 时,XCode 中发出警告 (4认同)
  • 它仍然调用 uiViewController.doSomething() 两次 (2认同)

nrj*_*nrj 7

这是我想出的一种方法,不会导致“在视图更新期间修改状态,这将导致未定义的行为”问题。技巧是将 ViewModel 的引用传递到 ViewController 本身,然后重置在那里调用函数的布尔值,而不是在 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
    }
}

Run Code Online (Sandbox Code Playgroud)


cbj*_*rup 3

您可以将 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
Run Code Online (Sandbox Code Playgroud)

更新了答案以更好地适合问题。