Zorayr
Zorayr

Reputation: 24874

SwiftUI NavigationLink with custom binding based on dictionary fails to reset after transitioning back

I have a dictionary that stores string -> bool, in my view, I wrap this dictionary around a Binding<Bool> to be able to store the states of each NavigationLink in order to later be able to programmatically navigate. Now, this works the first time, Page 1 -> Page 2 -> Page 3, but when you hit back to Page 2, the navigation stops working.

I have checked that the values of isLinkActive dictionary do correctly get updated when NavigationLink is tapped, however, NavigationLink does not get activated.

import SwiftUI

class ContentViewModel: ObservableObject {
    @Published var isLinkActive:[String: Bool] = [:]
}

struct ContentView: View {

    @ObservedObject var contentViewModel = ContentViewModel()

    var page3: some View {
        Text("Page 3")
    }

    @State var data = ["1", "2", "3"]

    func binding(chatId: String) -> Binding<Bool> {
        return .init(get: { () -> Bool in
            return self.contentViewModel.isLinkActive[chatId, default: false]
        }) { (value) in
            self.contentViewModel.isLinkActive[chatId] = value
        }
    }

    var page2: some View {
        return
            List(data, id: \.self) { data in
                NavigationLink(destination: self.page3, isActive: self.binding(chatId: data)) {
                    Text("Page \(data) Link")
                }
        }
    }

    var body: some View {
        return NavigationView() {
            VStack {
                Text("Page 1")
                NavigationLink(destination: page2) {
                    Text("Page 2 Link")
                }
            }
        }
    }
}

Upvotes: 2

Views: 1437

Answers (1)

Pranav Kasetti
Pranav Kasetti

Reputation: 9915

There's nothing wrong with the logic of your code. The only issue is that SwiftUI doesn't redraw page2 as it only redraws the var body during an onAppear, and this view is not in context. The way to force refresh is thus to make page2 a body variable.

struct ContentView: View {

    var body: some View {
        return NavigationView() {
            VStack {
                Text("Page 1")
                NavigationLink(destination: ContentViewTwo()) {
                    Text("Page 2 Link")
                }
            }
        }
    }
}

struct ContentViewTwo: View {

  @ObservedObject var contentViewModel = ContentViewModel()

  var page3: some View {
      Text("Page 3")
  }

  @State var data = ["1", "2", "3"]

  func binding(chatId: String) -> Binding<Bool> {
      return .init(get: { () -> Bool in
          return self.contentViewModel.isLinkActive[chatId, default: false]
      }) { (value) in
          self.contentViewModel.isLinkActive[chatId] = value
      }
  }

  var body: some View {
      return
          List(data, id: \.self) { data in
              NavigationLink(destination: self.page3, isActive: self.binding(chatId: data)) {
                  Text("Page \(data) Link")
              }
      }
  }
}

Upvotes: 1

Related Questions