UIKit UIViewController 将 Binding 传递给 SwiftUI View

ahe*_*eze 14 ios swift swiftui

我正在尝试在 UIKit 中提供 SwiftUI View。目前,我用一个Binding<Bool>来解雇/呈现DetailView。这是工作代码:

\n
struct ContentView: View {\n    @State var presentingModal = false\n    var body: some View {\n        Button("Present") { self.presentingModal = true } /// tap button to present detail sheet\n        .sheet(isPresented: $presentingModal) {\n            DetailView(isPresentedBinding: $presentingModal) /// pass in the binding\n        }\n    }\n}\n\nstruct DetailView: View {\n    var isPresented: Binding<Bool>? /// it's an optional so I do this instead of `@Binding var isPresented: Bool`\n    var body: some View {\n        Button("Done") { isPresented?.wrappedValue = false } /// tap the "Done" button to dismiss\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

当您按下“完成”按钮时,它会设置isPresented为 false,从而关闭该工作表。现在我想DetailView从一个UIViewController. 这是我到目前为止所拥有的:

\n
class ViewController: UIViewController {\n    @IBAction func presentButtonPressed(_ sender: Any) {\n        let detailViewController = DetailView_UIKit()\n        self.present(detailViewController, animated: true, completion: nil)\n    }\n}\n\n/// wrap DetailView in a ViewController so it's easier to present\nclass DetailView_UIKit: UIViewController {\n    \n    init() {\n        super.init(nibName: nil, bundle: nil)\n    }\n    \n    required init?(coder: NSCoder) {\n        fatalError("init(coder:) has not been implemented")\n    }\n    \n    override func loadView() {\n        \n        view = UIView()\n\n        /// Host the `DetailView`\n        let detailViewController = UIHostingController(\n            rootView: DetailView() /// I need to pass a Binding in here!\n        )\n        \n        self.addChild(detailViewController)\n        view.addSubview(detailViewController.view)\n        detailViewController.view.frame = view.bounds\n        detailViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n        detailViewController.didMove(toParent: self)\n    }\n    \n    /// I want to call this function too\n    func dismissThisViewController() {\n        self.dismiss(animated: true, completion: nil)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这有效,但是“完成”按钮没有执行任何操作...Binding仍然为零,因为我没有将它分配给任何东西。我如何在内部创建某种@State属性DetailView_UIKit以便我可以将其传递给DetailView?我希望dismissThisViewController在该属性设置为 时接到电话false。或者也许创建@State财产不是正确的方法,我不确定。

\n

SwiftUI 流程:

\n
ContentView (present)\xe2\x86\x92 DetailView\n        \xe2\x97\x9f________________\xe2\x97\x9e\n             Binding\n
Run Code Online (Sandbox Code Playgroud)\n

UIKit流程:

\n
ViewController (present)\xe2\x86\x92 DetailView_UIKit \xe2\x86\x92 DetailView\n                               \xe2\x97\x9f________________\xe2\x97\x9e\n                          Binding? Not sure what to put\n
Run Code Online (Sandbox Code Playgroud)\n

感谢您的帮助!

\n

ahe*_*eze 14

所以事实证明你不能Binding在 aUIViewController和之间使用 a View

最后,我只是在DetailView.

  • 如果我提出DetailView_UIKit,我会将闭包分配给一个解雇函数,但传入 a Binding(无论如何你都不能)。
  • 如果我DetailView直接呈现,我将像以前一样传递Binding,但分配闭包。

然后,当我按下里面的按钮时DetailView,我会尝试

  1. 将其设置Binding为 false
  2. 调用关闭

顺序并不重要,因为其中之一始终为零。

DetailView:

struct DetailView: View {
    var isPresented: Binding<Bool>? /// Optional `Binding` from before

    var donePressed: (() -> Void)? /// The optional closure I just added

    var body: some View {
        Button("Done") {

            /// Attempt to set the `Binding` (dismiss the `.sheet` if SwiftUI)
            isPresented?.wrappedValue = false

            /// Attempt to call the closure (call the `self.dismiss` if UIKit)
            donePressed?()

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

DetailView_UIKit:

override func loadView() {
    
    view = UIView()
    
    /// Host the `DetailView`
    let detailViewController = UIHostingController(
    rootView: DetailView() /// I DO NOT pass in a `Binding` here!
    )
    
    /// Assign the closure!
    detailViewController.donePressed = { [weak self] in
        self?.dismissThisViewController()
    }
    
    self.addChild(detailViewController)
    view.addSubview(detailViewController.view)
    detailViewController.view.frame = view.bounds
    detailViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    detailViewController.didMove(toParent: self)
}

/// Call this function in the closure
func dismissThisViewController() {
    self.dismiss(animated: true, completion: nil)
}
Run Code Online (Sandbox Code Playgroud)