简体   繁体   中英

How do I pass data from one SwiftUI view file to another?

The basic gist is I'm building a meal planner app. I'm new to Swift and SwiftUI and this is my first project so I'm probably making a noob mistake lol Basically I want to pass the current index, which is a data object for a day and it's meals, to the DailyMealPlan view when I tap on the corresponding card. I don't get an error when I pass the currentDay variable to the view, but inside the second view file I'm not sure how I handle that data. I keep getting an error that "CurrentMealPlan is not in scope". I understand how scope works and I suspect I'm just missing something when it comes to passing the data into the second view.

ForEach code

ForEach(CurrentMealPlan.indices) { index in
                                    
  let currentDay = CurrentMealPlan[index]
    NavigationLink(
      destination: DailyMealPlan(DailyMealPlan: currentDay))
    {                                  
      MealPlanCard(
       Day: "\(CurrentMealPlan[index].DayTitle)",
       Breakfast: "\(CurrentMealPlan[index].Breakfast)",
       Lunch: "\(CurrentMealPlan[index].Lunch)",
       Dinner: "\(CurrentMealPlan[index].Dinner)"
      )
    }
}

DailyMealPlan view

struct DailyMealPlan: View {
    
    var DailyMealPlan: Day = CurrentMealPlan[index]
    
    var body: some View {
    
        ZStack {
            ScrollView {
                VStack {
                   
                    SingleMealCard(Meal: "Breakfast", CalCount: 500, MealInfo: "Meal info here")
                    
                    SingleMealCard(Meal: "Lunch", CalCount: 500, MealInfo: "Meal info here")
                    
                    SingleMealCard(Meal: "Dinner", CalCount: 500, MealInfo: "Meal info here")
                }
            }
        }
    }
}

CurrentMealPlan model

struct Day: Hashable {
    var id: Int
    var Date: String
    var DayTitle: String
    var Breakfast: String
    var Lunch: String
    var Dinner: String
    
    
    init(id:Int=0,Date:String="",DayTitle:String="",Breakfast:String="",Lunch:String="",Dinner:String="") {
        self.id  = id
        self.Date = Date
        self.DayTitle = DayTitle
        self.Breakfast = Breakfast
        self.Lunch = Lunch
        self.Dinner = Dinner
    }
}

let CurrentMealPlan: [Day] = [
    Day(
        id: 0,
        DayTitle: "Sunday",
        Breakfast:"Oatmeal",
        Lunch: "Sandwich",
        Dinner: "Cheeseburger with Fries"
    )
]

Let's walk through your code. First, you declared the CurrentMealPlan array inside the parent view. It probably looks something like this:

@State var CurrentMealPlan = [Day]()

Note: As jnpdx commented , you should lowercase property names like var currentMealPlan and var dailyMealPlan . Also, you should not have the same name for the DailyMealPlan view and the DailyMealPlan property... it's extremely confusing.

Your code is correct. Then, you loop over CurrentMealPlan , and want to pass each element over to the DailyMealPlan view:

DailyMealPlan(DailyMealPlan: currentDay))

That's also completely fine. So where does the CurrentMealPlan is not in scope error come from? It's this line:

var DailyMealPlan: Day = CurrentMealPlan[index] /// NO!

Remember, you declared CurrentMealPlan inside the parent view, not the DailyMealPlan view. That means the DailyMealPlan view can't access CurrentMealPlan .

However, the DailyMealPlan view does not need to access CurrentMealPlan . Back in the parent view, you're already looping through CurrentMealPlan and passing in each currentDay over to the DailyMealPlan view.

So, all you need to do is define the type of the DailyMealPlan property:

struct DailyMealPlan: View {
   var DailyMealPlan: Day /// here!
   ...
}

This lets the compiler know that var DailyMealPlan takes in a Day .

I suggest using a MVVM approach, each screen view should have it's own view model (which should be a class extending a base view model or protocol) which you pass when you create the view.

In your case you should have:

protocol BaseViewModel {
}

class DailyMealPlanViewModel: BaseViewModel {
 @Published var day: Day

}

in the above I also added day as @Published so any changes on that will refresh the view, if the day never changes you can remove @Published.

in your view:

struct DailyMealPlan: View {
 @StateObject var viewModel: DailyMealPlanViewModel
 ...
 ...

And when navigating:

NavigationLink(
      destination: DailyMealPlan(viewModel: DailyMealPlanViewModel(day: currentDay))
    ...
    ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM