Present a New View in Swiftui

Present a new view in SwiftUI

To show a modal (iOS 13 style)

You just need a simple sheet with the ability to dismiss itself:

struct ModalView: View {
@Binding var presentedAsModal: Bool
var body: some View {
Button("dismiss") { self.presentedAsModal = false }
}
}

And present it like:

struct ContentView: View {
@State var presentingModal = false

var body: some View {
Button("Present") { self.presentingModal = true }
.sheet(isPresented: $presentingModal) { ModalView(presentedAsModal: self.$presentingModal) }
}
}

Note that I passed the presentingModal to the modal so you can dismiss it from the modal itself, but you can get rid of it.



To make it REALLY present fullscreen (Not just visually)

You need to access to the ViewController. So you need some helper containers and environment stuff:

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: UIViewController? {
get { return self[ViewControllerKey.self].value }
set { self[ViewControllerKey.self].value = newValue }
}
}

Then you should use implement this extension:

extension UIViewController {
func present<Content: View>(style: UIModalPresentationStyle = .automatic, @ViewBuilder builder: () -> Content) {
let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
toPresent.modalPresentationStyle = style
toPresent.rootView = AnyView(
builder()
.environment(\.viewController, toPresent)
)
NotificationCenter.default.addObserver(forName: Notification.Name(rawValue: "dismissModal"), object: nil, queue: nil) { [weak toPresent] _ in
toPresent?.dismiss(animated: true, completion: nil)
}
self.present(toPresent, animated: true, completion: nil)
}
}

Finally

you can make it fullscreen like:

struct ContentView: View {
@Environment(\.viewController) private var viewControllerHolder: UIViewController?

var body: some View {
Button("Login") {
self.viewControllerHolder?.present(style: .fullScreen) {
Text("Main") // Or any other view you like
// uncomment and add the below button for dismissing the modal
// Button("Cancel") {
// NotificationCenter.default.post(name: Notification.Name(rawValue: "dismissModal"), object: nil)
// }
}
}
}
}

Present modal view on navigation to another view in swiftui

There is a view modifier in SwiftUI known as .onAppear(perform:), which is the SwiftUI counterpart to UIKit's viewDidAppear and gives you an entry point upon the construction and display of a SwiftUI view. Simply add this modifier to View B (the view inside the sheet that you are presenting). In the closure that you provide to the modifier, you can change the state to present the picker as needed.

If you'd like the picker view to animate in after the view appears, the appropriate place to declare the transition and animation context is on the view acting as your picker view.

struct ViewB: View {

@State private var displayPicker = false

var body: some View {
VStack {
Text("This is View B")
if displayPicker {
PickerView()
.transition(.slide)
.animation(.easeInOut, value: displayPicker)
}
}
.onAppear {
displayPicker = true
}
}
}

Read more about the modifier here:
https://developer.apple.com/documentation/SwiftUI/AnyView/onAppear(perform:)

Present a new view when a condition has been met in SwiftUI

You can use the modifier .fullScreenCover

& you just need to pass a binding to a @State var which you set to true when you want to display the modal.

example

struct ExampleScreenView: View {
@State var showModal: Bool = false

var body: some View {
VStack {
Text("Some Text")
.padding()

Button {
showModal = true
} label: {
Text("Show other view")
}
}
.fullScreenCover(isPresented: $showModal) {
VStack {
Color.white
Text("Some other text")
.padding()

Button {
showModal = false
} label: {
Text("close view")
}
}
}
}
}

This example the first view has a button that sets the bool to true, and shows the modal, the modal view has a button that sets the bool to false and closes the view.

The button is just for the sake of an example, but you can use any logic to set the bool. to true, and then you can present whatever view you choose within the fullScreenCover.

Specific for the login scenario, you can use a .onReceive modifier to listen for a notification sent from your login successful code.

.onReceive(NotificationCenter.default.publisher(for: Notification.Name(rawValue: "didLogin"))

&

NotificationCenter.post(name:Notification.Name("didLogin"), object: nil)

Go to a new view using SwiftUI

The key is to use a NavigationView and a NavigationLink:

import SwiftUI

struct ContentView : View {
var body: some View {
NavigationView {
VStack {
Text("Hello World")
NavigationLink(destination: DetailView()) {
Text("Do Something")
}
}
}
}
}

How to Push View or Present View Modally in SwiftUI dynamically?

A possible solution is to create a custom enum with available presentation types:

enum PresentationType {
case push, sheet // ...
}

and create a custom binding for activating different views:

func showChildView(presentationType: PresentationType) -> Binding<Bool> {
.init(
get: { self.showChildView && self.presentationType == presentationType },
set: { self.showChildView = $0 }
)
}

Full code:

struct ContentView: View {
@State var presentationType = PresentationType.push
@State var showChildView = false

func showChildView(as presentationType: PresentationType) -> Binding<Bool> {
.init(
get: { self.showChildView && self.presentationType == presentationType },
set: { self.showChildView = $0 }
)
}

var body: some View {
NavigationView {
VStack {
Button(action: {
self.presentationType = .push
self.showChildView = true
}) {
Text("Present new view as Push")
}
Button(action: {
self.presentationType = .sheet
self.showChildView = true
}) {
Text("Present new view as Sheet")
}
}
.navigationBarTitle("Main view", displayMode: .inline)
.background(
NavigationLink(
destination: ChildView(),
isActive: self.showChildView(presentationType: .push),
label: {}
)
)
}
.sheet(isPresented: self.showChildView(presentationType: .sheet)) {
ChildView()
}
}
}

struct ChildView: View {
var body: some View {
ZStack {
Color.red
Text("Child view")
}
}
}

Trying to move to a new view in swiftUI after a button press

to make the "move work", add this to "AddAssignment":

struct AddAssignment: View {
@State var toAssignment: Int? = nil

replace your Button("Create New Task", action: {...}, with

        NavigationLink(destination: ViewAssignment(), tag: 1, selection: $toAssignment) {
Button("Create New Task") {
let task: [String] = [taskName, dueDate, subject, weighting, totalMarks]
print(task)
self.toAssignment = 1
}
}

make sure you wrap the whole thing in "NavigationView { ...}"

Show a new View from Button press Swift UI

For simple example you can use something like below

import SwiftUI

struct ExampleFlag : View {
@State var flag = true
var body: some View {
ZStack {
if flag {
ExampleView().tapAction {
self.flag.toggle()
}
} else {
OtherExampleView().tapAction {
self.flag.toggle()
}
}
}
}
}
struct ExampleView: View {
var body: some View {
Text("some text")
}
}
struct OtherExampleView: View {
var body: some View {
Text("other text")
}
}

but if you want to present more view this way looks nasty

You can use stack to control view state without NavigationView

For Example:

    class NavigationStack: BindableObject {
let didChange = PassthroughSubject<Void, Never>()

var list: [AuthState] = []

public func push(state: AuthState) {
list.append(state)
didChange.send()
}
public func pop() {
list.removeLast()
didChange.send()
}
}

enum AuthState {
case mainScreenState
case userNameScreen
case logginScreen
case emailScreen
case passwordScreen
}
struct NavigationRoot : View {
@EnvironmentObject var state: NavigationStack
@State private var aligment = Alignment.leading

fileprivate func CurrentView() -> some View {
switch state.list.last {
case .mainScreenState:
return AnyView(GalleryState())
case .none:
return AnyView(LoginScreen().environmentObject(state))
default:
return AnyView(AuthenticationView().environmentObject(state))
}
}
var body: some View {
GeometryReader { geometry in
self.CurrentView()
.background(Image("background")
.animation(.fluidSpring())
.edgesIgnoringSafeArea(.all)
.frame(width: geometry.size.width, height: geometry.size.height,
alignment: self.aligment))
.edgesIgnoringSafeArea(.all)
.onAppear {
withAnimation() {
switch self.state.list.last {
case .none:
self.aligment = Alignment.leading
case .passwordScreen:
self.aligment = Alignment.trailing
default:
self.aligment = Alignment.center
}
}
}
}
.background(Color.black)
}

}

struct ExampleOfAddingNewView: View {
@EnvironmentObject var state: NavigationStack
var body: some View {
VStack {
Button(action:{ self.state.push(state: .emailScreen) }){
Text("Tap me")
}

}
}
}

struct ExampleOfRemovingView: View {
@EnvironmentObject var state: NavigationStack
var body: some View {
VStack {
Button(action:{ self.state.pop() }){
Text("Tap me")
}
}
}
}

In my opinion this bad way, but navigation in SwiftUI much worse

SwiftUI: Go to a new view from inside a modal

I understood your question in 2 different ways, and luckily I have both solutions.

If you want the following flow: ViewA -> Opens ViewB As modal -> opens ViewC as NavigatedView in ViewB

Then all you need to change is your ModalView to this

struct ModalView: View {

@Binding var showModal: Bool

var body: some View {
NavigationView {
VStack (spacing: 30){
NavigationLink(destination: NewView()) {
Text("Take me to NewView")
}
Text("My Modal View")
.font(.title)
.padding()

Button("Close modal") {
self.showModal.toggle()
}

Button("Close modal and go to a new view") {

}

}
}
}
}

IF however what you mean is ViewA -> opens ViewsB as Moda -> ViewB then tells ViewA to navigate to ViewC

then please refer to this link, someone asked this question earlier and I provided the solution

SwiftUI transition from modal sheet to regular view with Navigation Link



Related Topics



Leave a reply



Submit