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:
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!
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.
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.
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.
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(\.
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.
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
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