UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

@Environment(\.presentationMode) causing child views to initialize multiple times

Forums > SwiftUI

I'm using @Environment(.presentationMode) in a parent view to allow for popping to root when navigating with TabView. This generally works alright, but it's causing some issues with child views that are forms. Indeed, this causes child views to initialize multiple times, sometimes a couple of seconds later. Bug can be replicated by the code below (just add into a basic app framework). Commenting out the @Enviroment line prevents the bug, with no other changes. I think the only solution I've got for now is to do the child view initialization tasks in onAppear, as this seems to only fire once.

Does anybody know what is the cause of this issue, and what can be done to fix it without the onAppear workaround? Thanks!

import SwiftUI

struct ContentView: View {
    @Environment(\.presentationMode) var presentationMode
    @State var presentModal: Bool = false

    var body: some View {
        Text("Hello, world!")
            .padding()
        Button("Press to load modal", action: {
            presentModal = true
        })
        .sheet(isPresented: $presentModal, content: {
            NestedView()
        })
    }
}

struct NestedView: View {
    init() {
        print("View initialized")
    }
    var body: some View {
        Text("Nested View")
            .padding()
            .onAppear(perform: {
                print("View appeared")
            })
    }
}

2      

I found that when you use @Environment(\.presentationMode) it some times cause problem, so I tend to use this where ever possible.

struct ContentView: View {
    //    @Environment(\.presentationMode) var presentationMode
    @State var presentModal: Bool = false

    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
            Button("Press to load modal") {
                presentModal = true
            }
        }
        .sheet(isPresented: $presentModal, content: {
            NestedView(presentModal: $presentModal)
        })
    }
}

struct NestedView: View {
    @Binding var presentModal: Bool
    // init with presentModal only because you had the print statement.
    // if not print then can delete all init
    init(presentModal: Binding<Bool>) {
        print("View initialized")
        self._presentModal = presentModal
    }

    var body: some View {
        VStack {
            Text("Nested View")
                .padding()
            // to change the boolean
            Button("Close") {
                presentModal = false
            }
        }
        .onAppear(perform: {
            print("View appeared")
        })
    }
}

As you see that "View initialized" is only printed once

2      

Right, that's the problem I'm having. I agree with you that avoiding init and using only a binding to a bool is preferred, but in cases where popping to the root is required, that's not possible without using the @Evironment, as far as I can tell. I guess one way that might be possible would be to track bindings for all sheets in the context, and dismiss any open modals when a tabview is selected, but that starts to get really spaghetti really quick.

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.