How to present a screen with modalPresentationStyle in SwiftUI like UIKit

Cuong (Currie) H. NGUYEN
3 min readSep 7, 2020

--

Hello, world! 🖐 I am Cuong (Currie) H. NGUYEN. I am a software engineer in Da Nang, Viet Nam. Now, I am working as an iOS developer.

SwiftUI, Swift, iOS

Today, I will share with you about how to show a screen with modalPresentationStyle in SwiftUI like UIKit.

While I am doing a SwiftUI project. SwiftUI has just released version 1, so I got some stuck about presenting a screen with fullscreen mode. It’s easy to do without NavigationView, but in a child NavigationView, I cannot present a fullscreen with available components of SwiftUI. So, that why I found out the way to solve it.

present screen over any screen
Demo

OK, let’s do it! 😊

Firstly, we will create an extension of UIApplication and add two methods to this file. The first is the method to get the topmost view controller.

func topMostController() -> UIViewController? {

guard
let window = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first,
let rootViewController = window.rootViewController else {
return nil
}

var topController = rootViewController

while let newTopController = topController.presentedViewController {
topController = newTopController
}

return topController
}

This method base on keyWindow to get the root view controller, then use while loop to get the topmost view controller.

The second method is to dismiss the screen.

func dismiss() {
UIApplication.shared.topMostController()!.dismiss(animated: true, completion: nil)
}

Next, we will create an extension of View. In this file, we will handle to present a screen.

func presentScreen<Content>(isPresented: Binding<Bool>, modalPresentationStyle: UIModalPresentationStyle, @ViewBuilder content: @escaping () -> Content) -> some View where Content : View{
if isPresented.wrappedValue {
let window = UIApplication.shared.windows.last
window?.isHidden = true
let view = content()
let viewController = UIHostingController(rootView: view)
viewController.modalPresentationStyle = modalPresentationStyle
UIApplication.shared.topMostController()!.present(viewController, animated: true, completion: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
isPresented.wrappedValue = false
}
}
return self
}

This method will transfer View to Controller by using UIHostingController and then present it in the topmost view controller. Here we can modify normally like UIKit.

So now, we done almost important things. Yeahhhh 😁!

Next, we will apply those methods above.

Let’s create ScreenBView.swift and add this code in.

struct ScreenBView: View {
var body: some View {
VStack{
Spacer()
HStack{
Spacer()
VStack{
Button(action: {
UIApplication.shared.dismiss()
}){
Text("Dismiss Screen B")
.foregroundColor(.white)
}
}
Spacer()
}
Spacer()
}
.background(Color.green)
}
}
struct ScreenBView_Previews: PreviewProvider {
static var previews: some View {
ScreenBView()
}
}

This file is easy to understand, right! So I don’t need to explain 😂.

In ContentView.swift, add this code in.

struct ContentView: View {
@State var isShow: Bool = false

var body: some View {
NavigationView {
VStack {
Spacer()
HStack {
Spacer()
Button(action: {
self.isShow = true
}){
Text("Show screen B")
}
Spacer()
}
Spacer()
}
.presentScreen(isPresented: $isShow, modalPresentationStyle: .overFullScreen, content: {
ScreenBView()
})
.navigationBarTitle(Text("Screen A"), displayMode: .inline)
}
}
}

So, Done!

Let’s run and enjoy 😋

Thank you for reading! Have a good life.

Give me a thumbs up if you are interesting 👍

--

--