Search code examples
swiftswiftuiswiftui-navigationlink

How to call SwiftUI NavigationLink conditionally?


I'm thinking how to conditionally permit to start NavigationLink when user has tapped it. In UIKit it is just obvious but in SwiftUI this seems to me a huge problem. I know that I could declare NavigationLink somewhere in my view i.e.

@State private var isFired = false

/* (...) */

NavigationLink(destination: AnotherView(), isActive: $isFired) { EmptyView() }

and call it like this

Button(action: {
    if /* check condition */ {
        self.isFired = true
    } else {
        print("condition is not fulfilled")
    }
}) {
    Image("my-image")
}

but... the problem is: my navigation links are created dynamically in a loop and are wrapping images in ScrollView, i.e.

ScrollView(.vertical) {
    ForEach(photos, id: \.self) { element in
        NavigationLink(destination: ImagePreviewView(photo: element)) {
            Image(uiImage: element.image)
        }
    }
}

How to conditionally allow to start the navigation link in the code above? For example: user tapps an image -> a password request is presented -> if the password is correct the navigation link fires, if not the app does something else (presents alert etc. doesn't matter)


Solution

  • Anyway it should be a button... something like

    ScrollView(.vertical) {
        ForEach(photos, id: \.self) { element in
            Button(action: {
               // here call a function with callback provided if password
               // verification passed
               self.checkPassword { verified in
                 if verified {
                    self.photo = element  // store tapped element
                    self.isFired = true   // << this might be called async
                 }
               }
            }) {
                Image(uiImage: element.image)
            }
        }
    }
    .background(
       // activate link for tapped photo
       NavigationLink(destination: ImagePreviewView(photo: self.photo), 
          isActive: $isFired) { EmptyView() }
    )