A background to the issue: I am creating a simple app which stores activity data in a struct. I have created a custom view called "CardView" (held in a separate file within the project) which is to appear onto a basic ContentView. The CardView is populated with Text labels which are to be bound to corresponding data within the aforementioned activity data struct. A ForEach handles the dynamic creation of these cards according to how many activity "items" exist. See following code...
struct ActivityItem: Identifiable, Codable {
let id = UUID()
let name: String
let description: String
let type: String
var amount: Int
}
class AppData: ObservableObject {
@Published var activites: [ActivityItem] {
didSet {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(activites) {
UserDefaults.standard.set(encoded, forKey: "Activites")
}
}
}
init() {
if let foundActivities = UserDefaults.standard.data(forKey: "Activites") {
let decoder = JSONDecoder()
if let decoded = try? decoder.decode([ActivityItem].self, from: foundActivities) {
self.activites = decoded
return
}
}
self.activites = []
}
}
struct ContentView: View {
@ObservedObject var appData = AppData()
@State private var showingAddActivity = false
@State private var showingMoreInfo = false
var body: some View {
NavigationView {
ZStack {
Rectangle()
.fill(Color(.gray))
.frame(maxWidth: .infinity, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
ScrollView {
VStack(spacing: 50) {
ForEach(appData.activites) { activity in
CardView(appData: self.appData)
}
}
}
// Below held in separate file
struct CardView: View {
@ObservedObject var appData: AppData
@State private var showingMoreInfo = false
var body: some View {
ZStack {
Rectangle()
.fill(Color(.gray))
.frame(maxWidth: .infinity, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
RoundedRectangle(cornerRadius: 20)
.fill(Color(.black))
.frame(width: 325, height: 180)
VStack {
Text("Name")
.font(.title)
Text("Description")
Text("Type")
Text("Amount")
}
.sheet(isPresented: $showingMoreInfo) {
Text("View")
}
}
}
}
The issue: Despite multiple attempts and countless hours and errors later through using @Binding and leveraging @Environmental, I cannot pass data from the current ActivityItem in the ForEach into the CardView to be used to replace the String placeholder in the Text labels (i.e. Name, Type, Amount). When trying to create and use bindings, such as something like $activity.name, it declares that "activity" cannot become a binding. What am I doing wrong? How, in the simplest way, can I get each activity to pass its data to the CardView struct, populate those Text labels and then create itself on the ContentView "main screen" and then repeat for all remaining objects in the ForEach iteration? Is there any important notes to keep in mind to avoid issues like this in the future? I must add that if I put the CardView code directly into ContentView and not call CardView() in the ForEach, I can easily refer to the data; it only becomes an issue when I use the CardView struct (a long term goal for reusability and less code clutter). Thank you for your help!
CardView
, since it's only displaying - not modifying - the values, it doesn't need a binding. Just create a propertylet activity: ActivityItem
, and pass it in init:CardView(appData: self.appData, activity: activity)
. Do you even needappData
insideCardView
? If not, remove that, and it becomesCardView(activity: activity)
- New Dev