Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to run `task` or `onAppear` on NavigationSplitView detail for every selection change?

I’m trying to use NavigationSplitView with a DetailView that has a task or onAppear in it, but it seems it only runs once.

enum MenuItem: String, Hashable, Identifiable, CaseIterable {
    case menu1
    case menu2
    case menu3
    case menu4
    case menu5
    
    var id: String { rawValue }
}

struct ContentView: View {
    @State var selection: MenuItem?
    
    var body: some View {
        NavigationSplitView {
            List(MenuItem.allCases, selection: $selection) { item in
                NavigationLink(value: item) {
                    Text(item.rawValue)
                }
            }
        } detail: {
            if let selection {
                DetailView(menuItem: selection)
            } else {
                Text("Default")
            }
        }
    }
}

struct DetailView: View {
    let menuItem: MenuItem
    @State var name = "Name"
    
    var body: some View {
        VStack {
            Text(menuItem.id)
            Text(name)
        }
        .task {
            // This should be an async setup code
            // but for the sake of simplicity
            // I just made it like this
            name = menuItem.id
        }
    }
}

Initial application load
enter image description here

Initial menu selection
enter image description here

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

2nd to 5th menu selection
enter image description here

I know I can use onChange(of: selection) as a workaround, and then have my setup code there. But is there any other way to make task or onAppear work inside my DetailView?

>Solution :

It is SwiftUI optimization, it recreates only dependent parts.

A possible solution is to make entire body dependent on menu item, so it will be recreated completely and calls task again, like

struct DetailView: View {
    let menuItem: MenuItem
    @State var name = "Name"
    
    var body: some View {
        VStack {
            Text(menuItem.id)
            Text(name)
        }
        .task {
            // This should be an async setup code
            // but for the sake of simplicity
            // I just made it like this
            name = menuItem.id
        }
        .id(menuItem.id) // << here !!
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading