TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

@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      

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

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.