sfung3
sfung3

Reputation: 2387

Show NavigationLink based on condition and having value edited inside the destination

I have a ForEach loop for my StudentStore and only want to display them in my list if the isFavorite field is true.

The problem is when I edit the value of isFavorite inside SecondView, it goes back to ContentView because of the if statement inside ContentView.

I want the list to update when I get back to ContentView rather than updating it immediately causing it to go back to the original page.

enter image description here

When I was looking for a solution I came upon this:

Read this entire thread thinking you were asking if you could make a NavigationView conditionally hidden and thought, “That sounds like a mess...”
Source: https://www.reddit.com/r/SwiftUI/comments/e2wn09/make_navigationlink_conditional_based_on_bool/

Can someone also how to solve this problem and why conditionally hiding a NavigationView is a bad idea?

Model

import SwiftUI

struct StudentItem: Identifiable {
    var id = UUID()
    var name: String
    var isFavorite: Bool

    init(name: String, isFavorite: Bool) {
        self.name = name
        self.isFavorite = isFavorite
    }
}

class StudentStore: ObservableObject {
    @Published var StudentStore: [StudentItem]

    init(StudentStore: [StudentItem]){
        self.StudentStore = StudentStore
    }
}

Main view

struct ContentView: View {
    @ObservedObject var studentStore = StudentStore(StudentStore: [StudentItem(name: "Stephen", isFavorite: true),
                                                                   StudentItem(name: "Jay", isFavorite: true),
                                                                   StudentItem(name: "Isaac", isFavorite: true),
                                                                   StudentItem(name: "Talha", isFavorite: true),
                                                                   StudentItem(name: "John", isFavorite: true),
                                                                   StudentItem(name: "Matt", isFavorite: true),
                                                                   StudentItem(name: "Leo", isFavorite: true)])

    var body: some View {
        NavigationView {
            List {
                ForEach(studentStore.StudentStore.indices, id: \.self) { index in
                    Group {
                        if self.studentStore.StudentStore[index].isFavorite == true {
                            NavigationLink(destination: SecondView(student: self.$studentStore.StudentStore[index])) {
                                HStack {
                                    Text(self.studentStore.StudentStore[index].name)
                                    Image(systemName: self.studentStore.StudentStore[index].isFavorite ? "star.fill" : "star")
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Sub view

struct SecondView: View {
    @Binding var student: StudentItem

    var body: some View {
        Button(action: {
            self.student.isFavorite.toggle()
        }) {
            Image(systemName: student.isFavorite ? "star.fill" : "star")
        }
    }
}

Upvotes: 0

Views: 316

Answers (1)

Danny B
Danny B

Reputation: 93

The problem makes sense, it's a Binded value. The Navigation to that person SecondView technically no longer exists according to the Binding.

You could create a localized State for SecondView, set your Image and Button to work that that property, and then use onDisappear to update the StudentItem as you Navigate back.

struct SecondView: View {
    @Binding var student: StudentItem
    @State var isFavorite: Bool = true

    var body: some View {
        Button(action: {
            self.isFavorite.toggle()
        }) {
            Image(systemName: self.isFavorite ? "star.fill" : "star")
        }.onDisappear {
            self.student.isFavorite = self.isFavorite
        }
    }
}

The above will work with the rest of your code.

As for not using an if statement, I kind of get it. For me, I would want to iterate through the values I know I want to include. Not saying this is perfect, but you could filter the list before you go in, and then summon the Bindable reference to the StudentItem to pass into SecondView. Be sure to make StudentItem Hashable for this to work:

var body: some View {
        NavigationView {
            List {
                ForEach(studentStore.StudentStore.filter({$0.isFavorite == true}), id: \.self) { student in
                    Group {
                        NavigationLink(destination: SecondView(student: self.$studentStore.StudentStore[self.studentStore.StudentStore.firstIndex(of: student)!])) {
                                HStack {
                                    Text(student.name)
                                    Image(systemName: student.isFavorite ? "star.fill" : "star")
                                }
                            }
                        }
                }
            }
        }
    }

Upvotes: 1

Related Questions