在ForEach迭代(SwiftUI)中将数据传递到自定义视图中

时间:2020-08-08 05:06:02

标签: swift swiftui

问题的背景:我正在创建一个简单的应用,该应用将活动数据存储在结构中。我创建了一个自定义视图,称为“ CardView”(保存在项目中的单独文件中),该视图将显示在基本ContentView上。 CardView填充有Text标签,该Text标签将绑定到上述活动数据结构中的相应数据。 ForEach根据存在的活动“项目”处理这些卡片的动态创建。请参阅以下代码...

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")
        }
    }
}
}

问题:尽管后来通过使用@Binding和利用@Environmental进行了多次尝试,无数小时和错误,但我无法将数据从ForEach中的当前ActivityItem传递到CardView中,以用于替换Text标签中的String占位符(即名称,类型,金额)。尝试创建和使用绑定时,例如$ activity.name之类的绑定,它声明“ activity”不能成为绑定。我究竟做错了什么?如何以最简单的方式让每个活动将其数据传递给CardView结构,填充这些Text标签,然后在ContentView“主屏幕”上创建自己,然后对ForEach迭代中的所有其余对象重复?有什么重要的注意事项要记住,以免将来发生此类问题?我必须补充一点,如果我将CardView代码直接放入ContentView中而不在ForEach中调用CardView(),则可以轻松地引用数据;仅在使用CardView结构时才成为问题(可重用性和代码混乱的长期目标)。谢谢您的帮助!

1 个答案:

答案 0 :(得分:0)

我假设您的意思是以下内容(已通过Xcode 12 / iOS 14测试)

struct ActivityItem: Identifiable, Codable {
var id = UUID()
var name: String            // << made `var` here and other to allow write
var description: String
var 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.indices, id: \.self) { i in
                        CardView(item: $appData.activites[i])  // binding here  
                    }
                }
            }
        }
    }
}
}

// Below held in separate file
struct CardView: View {
@Binding var item: ActivityItem             // binding here
@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 {
            TextField("Name", text: $item.name)    // edit here
                .font(.title)

            Text("Description")

            Text("Type")

            Text("Amount")
        }
        .sheet(isPresented: $showingMoreInfo) {
            Text("View")
        }
    }
}
}