Search code examples
swiftxcodeswiftuistateswiftui-navigationlink

Navigation Link - Pass Specific Array Element To New View via NavigationLink & Lists


I have an array titled recipeArray that is displayed in a list. When a list item is clicked, the user is taken to a new view titled RecipeItemDetailView. The array contains some default values as placeholders.

struct StoredRecipeModel: Identifiable {
var id = UUID()
var name: String
var recipeSteps: [String]

}

struct ContentView: View {

@State var recipeArray: [StoredRecipeModel] = [StoredRecipeModel(name: "one", recipeSteps: ["step1 here", "step1b", "Step1c"]), StoredRecipeModel(name: "two", recipeSteps: ["step2here"]), StoredRecipeModel(name: "three", recipeSteps: ["Step3here"])]

var body: some View {
    NavigationView {
            VStack {
                List(recipeArray, id: \.id) { index in NavigationLink(destination: RecipeItemDetailView(recipe: $recipeArray, listItem: ListIndex.init())) {
                        HStack {
                            Text(index.name)
                            
                        }
                    }
                }

In the RecipeItemDetailView (the view the user navigates to when clicking on a list item), I want to only display the data for a given element. For example, if the user clicks on the first item in the list (labeled "One"), I would only like to display the recipeSteps for that given element - in this case being ["step1 here", "step1b", "Step1c"].

My current code passes forward the entire array. Is there a way to only pass forward the elements tied to a given index - e.g. the index that is clicked on?

EDIT - adding additional RecipeItemDetailView code

import SwiftUI

struct RecipeItemDetailView: View {

@Binding var recipe: [StoredRecipeModel]
@Environment(\.presentationMode) var presentationMode
//@ObservedObject var listItem: ListIndex
var recipeDataToReturn = 0


var body: some View {
    
    var stored = [recipe[recipeDataToReturn].recipeSteps]
    
    NavigationView {
        VStack {
            List(stored[0], id: \.self) { index in Text(index)}
   
            List {
                Section(header: Text("Section")) {
                    Button("print") {
                        print(stored)
                    }
                }
            }

        }
    }
}

}


Solution

  • If you just need the recipe and no Binding to it, it's very easy -- just pass the variable you get in the List closure to your detail view:

    struct ContentView: View {
        
        @State var recipeArray: [StoredRecipeModel] = [StoredRecipeModel(name: "one", recipeSteps: ["step1 here", "step1b", "Step1c"]), StoredRecipeModel(name: "two", recipeSteps: ["step2here"]), StoredRecipeModel(name: "three", recipeSteps: ["Step3here"])]
        
        var body: some View {
            NavigationView {
                VStack {
                    List(recipeArray, id: \.id) { recipe in
                        NavigationLink(destination: RecipeItemDetailView(recipe: recipe)) {
                            HStack {
                                Text(recipe.name)
                            }
                        }
                    }
                }
            }
        }
    }
    struct RecipeItemDetailView : View {
        var recipe: StoredRecipeModel
        
        var body: some View {
            Text(recipe.name)
        }
    }
    
    

    If you need a Binding, you can get that with the index:

    
    struct ContentView: View {
        
        @State var recipeArray: [StoredRecipeModel] = [StoredRecipeModel(name: "one", recipeSteps: ["step1 here", "step1b", "Step1c"]), StoredRecipeModel(name: "two", recipeSteps: ["step2here"]), StoredRecipeModel(name: "three", recipeSteps: ["Step3here"])]
        
        var body: some View {
            NavigationView {
                VStack {
                    List(recipeArray.indices, id: \.self) { index in
                        NavigationLink(destination: RecipeItemDetailView(recipe: $recipeArray[index])) {
                            HStack {
                                Text(recipeArray[index].name)
                            }
                        }
                    }
                }
            }
        }
    }
    
    struct RecipeItemDetailView : View {
        @Binding var recipe: StoredRecipeModel
        
        var body: some View {
            Text(recipe.name)
        }
    }
    

    Or, write a Binding to tie the id to the element:

    
    struct ContentView: View {
        
        @State var recipeArray: [StoredRecipeModel] = [StoredRecipeModel(name: "one", recipeSteps: ["step1 here", "step1b", "Step1c"]), StoredRecipeModel(name: "two", recipeSteps: ["step2here"]), StoredRecipeModel(name: "three", recipeSteps: ["Step3here"])]
        
        func bindingForId(id: UUID) -> Binding<StoredRecipeModel> {
            .init {
                recipeArray.first(where: { $0.id == id}) ?? StoredRecipeModel(name: "", recipeSteps: [])
            } set: { newValue in
                recipeArray = recipeArray.map {
                    return $0.id == id ? newValue : $0
                }
            }
        }
        
        var body: some View {
            NavigationView {
                VStack {
                    List(recipeArray, id: \.id) { recipe in
                        NavigationLink(destination: RecipeItemDetailView(recipe: bindingForId(id: recipe.id))) {
                            HStack {
                                Text(recipe.name)
                            }
                        }
                    }
                }
            }
        }
    }
    
    struct RecipeItemDetailView : View {
        @Binding var recipe: StoredRecipeModel
        
        var body: some View {
            Text(recipe.name)
        }
    }
    

    Update, based on comments:

    struct RecipeItemDetailView : View {
        @Binding var recipe: StoredRecipeModel
        
        var body: some View {
            NavigationView {
                VStack {
                    List(recipe.recipeSteps, id: \.self) { item in
                        Text(item)
                    }
                    
                    List {
                        Section(header: Text("Section")) {
                            Button("print") {
                                print(recipe)
                            }
                        }
                    }
                }
            }
        }
    }