Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI - PresentationButton with modal that is full screen

I am trying to implement a button that presents another scene with a "Slide from Botton" animation.

PresentationButton looked like a good candidate, so I gave it a try:

import SwiftUI

struct ContentView : View {
    var body: some View {
        NavigationView {
            PresentationButton(destination: Green().frame(width: 1000.0)) {
                Text("Click")

                }.navigationBarTitle(Text("Navigation"))
        }
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        Group {
            ContentView()
                .previewDevice("iPhone X")
                .colorScheme(.dark)

            ContentView()
                .colorScheme(.dark)
                .previewDevice("iPad Pro (12.9-inch) (3rd generation)"

            )

        }

    }
}
#endif

And here is the result: enter image description here

I want the green view to cover the whole screen, and also the modal to be not "draggable to close".

Is it possible to add modifier to PresentationButton to make it full screen, and not draggable?

I have also tried a Navigation Button, but: - It doesn't "slide from bottom" - It creates a "back button" on detail view, which I don't want

thanks!

like image 479
Mane Manero Avatar asked Jun 25 '19 14:06

Mane Manero


People also ask

How do I present a view in full screen SwiftUI?

How to present a full screen modal view using fullScreenCover() SwiftUI's fullScreenCover() modifier gives us a presentation style for times when you want to cover as much of the screen as possible, and in code it works almost identically to regular sheets.

What is a full screen modal?

Fullscreen modal is a modal window, which opens on top of the webpage. It blocks all functions of the parent page and has a separate URL.

How do I present a view in Modally SwiftUI?

Modal views in SwiftUI are presented using the sheet modifier on a view or control. The simplest way is to have @State property to indicate when it should be visible. To hide the modal view, we can use the environment parameter or pass a binding to the modal view object.

How do I use sheets in SwiftUI?

To use a sheet, give it something to show (some text, an image, a custom view, etc), add a Boolean that defines whether the detail view should be showing, then attach it to your main view as a modal sheet. Important: If you're targeting iOS 14 or below, you should use @Environment(\.


2 Answers

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.

like image 98
arsenius Avatar answered Oct 02 '22 19:10

arsenius


Xcode 12.0 - SwiftUI 2 - iOS 14

Now possible. Use fullScreenCover() modifier.

var body: some View {
    Button("Present!") {
        self.isPresented.toggle()
    }
    .fullScreenCover(isPresented: $isPresented, content: FullScreenModalView.init)
}

Hacking With Swift

like image 12
Eric Avatar answered Sep 28 '22 19:09

Eric