Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to programmatically trigger NavigationLink?

Tags:

swiftui

I have a NavigationView with a bunch of NavigationLinks in it. When the user hits the "Add" button, I want to create a new item in the view and automatically send them to it. Can I do that?

like image 739
Heilemann Avatar asked Jul 29 '19 03:07

Heilemann


People also ask

How would you create programmatic navigation in SwiftUI?

NavigationLink in SwiftUI allows pushing a new destination view on a navigation controller. You can use NavigationLink in a list or decide to push a view programmatically. The latter enables you to trigger a new screen from a different location in your view.

Is SwiftUI programmatic?

By default, the various navigation APIs that SwiftUI provides are very much centered around direct user input — that is, navigation that's handled by the system in response to events like button taps and tab switching.

How do I push in SwiftUI?

If you have a navigation view and you want to push a new view onto SwiftUI's navigation stack, you should use NavigationLink .


1 Answers

SwiftUI triggers View changes (the body) when its source of truth changes, so you just need to change the Binding<Bool> value to trigger a view update. Store the bindings for each link, and change its value when you click a button.

Let's say you have three different views you want to push. Then you would have three different bindings to store. You can store these bindings as three separate @State properties, or using a @StateObject view model as follows:

class RootViewModel: ObservableObject {
  @Published var isLinkActive:[Int: Bool] = [:]
}

struct RootView: View {
  @StateObject var viewModel = RootViewModel()
  ...
  func binding(index: Int) -> Binding<Bool> {
    return .init(get: { () -> Bool in
      return self.viewModel.isLinkActive[index, default: false]
    }) { (value) in
      self.viewModel.isLinkActive[index] = value
    }
  }
  ...
  func destination(index: Int) -> some View {
    switch index {
      case 1:
        return ContentViewOne()
      case 2:
        return ContentViewTwo()
      case 3:
        return ContentViewThree()
      default:
        return EmptyView()
    }
  }
  
  var body: some View {
    return
      NavigationView {
        VStack {
          ForEach(1..<4) { index in
            NavigationLink(destination: self.destination(index: index), isActive: self.binding(index: index)) {
              Text("Link to Content View \(index)")
            }
          }
          Button("Add") {
            self.contentViewModel.isLinkActive[2] = true // Change this index depending on which view you want to push.
          }
        }
      }
  }
}

So the button here simulates the second NavigationLink tap. I used StateObject to stop redrawing the whole RootView in the middle of a push.

like image 152
Pranav Kasetti Avatar answered Sep 29 '22 10:09

Pranav Kasetti