SwiftUI - PresentationButton with modal that is full screen

Unfortunately, as of Beta 2 Beta 3, this is not possible in pure SwiftUI. You can see that Modal has no parameters for anything like UIModalPresentationStyle.fullScreen. Likewise for PresentationButton.

I suggest filing a radar.

The nearest you can currently do is something like:

    @State var showModal: Bool = false
    var body: some View {
        NavigationView {
            Button(action: {
                self.showModal = true
            }) {
                Text("Tap me!")
            }
        }
        .navigationBarTitle(Text("Navigation!"))
        .overlay(self.showModal ? Color.green : nil)
    }

Of course, from there you can add whatever transition you like in the overlay.


Although my other answer is currently correct, people probably want to be able to do this now. We can use the Environment to pass a view controller to children. Gist here

struct ViewControllerHolder {
    weak var value: UIViewController?
}


struct ViewControllerKey: EnvironmentKey {
    static var defaultValue: ViewControllerHolder { return ViewControllerHolder(value: UIApplication.shared.windows.first?.rootViewController ) }
}

extension EnvironmentValues {
    var viewController: UIViewControllerHolder {
        get { return self[ViewControllerKey.self] }
        set { self[ViewControllerKey.self] = newValue }
    }
}

Add an extension to UIViewController

extension UIViewController {
    func present<Content: View>(style: UIModalPresentationStyle = .automatic, @ViewBuilder builder: () -> Content) {
        // Must instantiate HostingController with some sort of view...
        let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
        toPresent.modalPresentationStyle = style
        // ... but then we can reset rootView to include the environment
        toPresent.rootView = AnyView(
            builder()
                .environment(\.viewController, ViewControllerHolder(value: toPresent))
        )
        self.present(toPresent, animated: true, completion: nil)
    }
}

And whenever we need it, use it:

struct MyView: View {

    @Environment(\.viewController) private var viewControllerHolder: ViewControllerHolder
    private var viewController: UIViewController? {
        self.viewControllerHolder.value
    }

    var body: some View {
        Button(action: {
           self.viewController?.present(style: .fullScreen) {
              MyView()
           }
        }) {
           Text("Present me!")
        }
    }
}

[EDIT] Although it would be preferable to do something like @Environment(\.viewController) var viewController: UIViewController? this leads to a retain cycle. Therefore, you need to use the holder.