What is the best way to modally show a SwiftUI view from any class or structure? I use a UIHostingController from UIKit. Is there any better way to do this using only SwiftUI?
ContentView with buttons to present the SwiftUI view
struct ContentView: View {
var body: some View {
Button {
// Present the view
presentView(controller: UIHostingController(rootView: view))
} label: {
Text("Present view")
}
}
var view: some View {
Button {
// Dismiss the view
dismissView()
} label: {
Rectangle()
.overlay(
Text("Dismiss view")
)
}
}
}
The functions used to present the SwiftUI view
extension ContentView {
// Returns the top view controller
func topViewController() -> UIViewController? {
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
if var topController = keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController { topController = presentedViewController }
return topController
} else { return nil }
}
// Presents the SwiftUI view in a UIHostingController
func presentView(controller: UIViewController) {
controller.view.backgroundColor = .none
controller.modalPresentationStyle = .overCurrentContext
topViewController()?.present(controller, animated: false, completion: nil)
}
// Removes the UIHostingViewController from root view
func dismissView() {
topViewController()?.dismiss(animated: false, completion: nil)
}
}
You can present modal sheet like this:
struct ContentView: View {
@State private var showSheet = false
var body: some View {
Button("Present") {
showSheet.toggle()
}.font(.largeTitle)
.sheet(isPresented: $showSheet) {
SheetView()
}
}
}
struct SheetView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
ZStack {
Button {
dismiss()
} label: {
Image(systemName: "xmark.circle")
.font(.largeTitle)
.foregroundColor(.gray)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
.padding()
}
}
Or present modal on full screen like this:
struct ContentView: View {
@State private var showSheet = false
var body: some View {
Button("Present") {
showSheet.toggle()
}.font(.largeTitle)
.fullScreenCover(isPresented: $showSheet) {
SheetView()
}
}
}
struct SheetView: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
ZStack {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "xmark.circle")
.font(.largeTitle)
.foregroundColor(.gray)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
.padding()
}
}
For your comment it's no problem, you can just do this:
struct ContentView: View {
@State private var showSheet = false
var body: some View {
ZStack {
Button("Present") {
showSheet.toggle()
}
.font(.largeTitle)
if showSheet {
ZStack {
Button {
showSheet.toggle()
} label: {
Image(systemName: "xmark.circle")
.font(.largeTitle)
.foregroundColor(.gray)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
.padding()
}
.background(.ultraThickMaterial)
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With