Reputation: 30336
I'm trying to make a SwiftUI View
available in UIKit. Currently, there's a Binding<Bool>
that I use for dismissing/presenting DetailView
. Here's the working code:
struct ContentView: View {
@State var presentingModal = false
var body: some View {
Button("Present") { self.presentingModal = true } /// tap button to present detail sheet
.sheet(isPresented: $presentingModal) {
DetailView(isPresentedBinding: $presentingModal) /// pass in the binding
}
}
}
struct DetailView: View {
var isPresented: Binding<Bool>? /// it's an optional so I do this instead of `@Binding var isPresented: Bool`
var body: some View {
Button("Done") { isPresented?.wrappedValue = false } /// tap the "Done" button to dismiss
}
}
When you press the "Done" button, it sets isPresented
to false, which dismisses the sheet. Now I want to present DetailView
from a UIViewController
. This is what I have so far:
class ViewController: UIViewController {
@IBAction func presentButtonPressed(_ sender: Any) {
let detailViewController = DetailView_UIKit()
self.present(detailViewController, animated: true, completion: nil)
}
}
/// wrap DetailView in a ViewController so it's easier to present
class DetailView_UIKit: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = UIView()
/// Host the `DetailView`
let detailViewController = UIHostingController(
rootView: DetailView() /// I need to pass a Binding in here!
)
self.addChild(detailViewController)
view.addSubview(detailViewController.view)
detailViewController.view.frame = view.bounds
detailViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
detailViewController.didMove(toParent: self)
}
/// I want to call this function too
func dismissThisViewController() {
self.dismiss(animated: true, completion: nil)
}
}
This works, but the "Done" button doesn't do anything... the Binding
is still nil because I haven't assigned it to anything. How can I create some sort of @State
property inside DetailView_UIKit
so that I can pass it into DetailView
? I would like dismissThisViewController
to be called when that property is set to false
. Or maybe creating an @State
property isn't the right approach, I'm not sure.
ContentView (present)→ DetailView
◟________________◞
Binding
ViewController (present)→ DetailView_UIKit → DetailView
◟________________◞
Binding? Not sure what to put
Thanks for your help!
Upvotes: 17
Views: 8992
Reputation: 30336
Binding
between a UIViewController
and View
.In the end, I just made an optional closure in DetailView
.
DetailView_UIKit
, I will assign the closure to a dismiss function, but not pass in a Binding
(you can't anyway).DetailView
directly, I will pass in the Binding
as before, but not assign the closure.Then, when I press the button inside DetailView
, I will attempt to
Binding
to falseThe order doesn't matter, because one of them will always be nil.
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?()
}
}
}
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)
}
Upvotes: 14