Maschina
Maschina

Reputation: 926

Clear SwiftUI list in NavigationView does not properly go back to default

The simple navigation demo below demonstrate an example of my issue:

import SwiftUI

// Data model
class Model: ObservableObject {
    // Example data
    @Published var example: [String] = ["Data 1", "Data 2", "Data 3"]
    @Published var selected: String?
}

// View
struct ContentView: View {
    @ObservedObject var data: Model = Model()
    
    var body: some View {
        VStack {
            // button to empty data set
            Button(action: {
                data.selected = nil
                data.example.removeAll()
            }) {
                Text("Empty Example data")
            }
            
            NavigationView {
                // data list
                List {
                    ForEach(data.example, id: \.self) { element in
                        // navigation to "destination"
                        NavigationLink(destination: destination(element: element), tag: element, selection: $data.selected) {
                            Text(element)
                        }
                    }
                }
                
                // default view when nothing is selected
                Text("Nothing selected")
            }
        }
    }
    
    func destination(element: String) -> some View {
        return Text("\(element) selected")
    }
}

enter image description here

What happens when I click the "Empty Example data" button is that the list will be properly cleared up. However, the selection is somehow persistent and the NavigationView will not jump back to the default view when nothing is selected:

enter image description here

I would expect the view Text("Nothing selected") being loaded.

Do I overlook something important?

Upvotes: 0

Views: 1070

Answers (1)

When you change the data.example in the button, the left panel changes because the List has changed. However the right side do not, because no change has occurred there, only the List has changed. The ForEach does not re-paint the "destination" views.

You have an "ObservedObject" and its purpose is to keep track of the changes, so using that you can achieve what you want, like this:

import SwiftUI

@main
struct TestApp: App {
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class Model: ObservableObject {
    @Published var example: [String] = ["Data 1", "Data 2", "Data 3"]
    @Published var selected: String?
}

struct ContentView: View {
    @StateObject var data: Model = Model()

    var body: some View {
        VStack {
            Button(action: {
                data.selected = nil
                data.example.removeAll()
            }) {
                Text("Empty Example data")
            }
            NavigationView {
                List {
                    ForEach(data.example, id: \.self) { element in
                        NavigationLink(destination: DestinationView(),
                                       tag: element,
                                       selection: $data.selected) {
                            Text(element)
                        }
                    }
                }
                Text("Nothing selected")
            }.environmentObject(data)
        }
    }
}

struct DestinationView: View {
    @EnvironmentObject var data: Model
    var body: some View {
        Text("\(data.selected ?? "")")
    }
}

Upvotes: 1

Related Questions