我有一份水果清单。结构体
FruitRowView
提供每行视图的布局。在这个FruitRowView
中,有一个TextField
,我想显示每种水果的名称。我在做这件事时遇到了麻烦。我之所以想使用TextField
而不是Text
来显示每个水果的名称,是为了让用户可以轻松地从TextField
编辑水果的名称。在本例中,水果是核心数据实体,水果名称是该实体的属性。
这是我的核心数据类:
class CoreDataViewModel: ObservableObject {
let container: NSPersistentContainer
@Published var savedEntities: [FruitEntity] = []
init() {
container = NSPersistentContainer(name: "FruitsContainer")
container.loadPersistentStores { (description, error) in
if let error = error {
print("Error with coreData. \(error)")
}
}
fetchFruits()
}
func fetchFruits() {
let request = NSFetchRequest<FruitEntity>(entityName: "FruitEntity")
do {
savedEntities = try container.viewContext.fetch(request)
} catch let error {
print("Error fetching. \(error)")
}
}
func addFruit(text: String) {
let newFruit = FruitEntity(context: container.viewContext)
newFruit.name = text
saveData()
}
func saveData() {
do {
try container.viewContext.save()
fetchFruits()
} catch let error {
print("Error saving. \(error)")
}
}
}
这是我的内容视图:
struct ContentView: View {
//sheet variable
@State var showSheet: Bool = false
@StateObject var vm = CoreDataViewModel()
@State var refresh: Bool
var body: some View {
NavigationView {
VStack(spacing: 20) {
Button(action: {
showSheet.toggle()
}, label: {
Text("Add Fruit")
})
List {
ForEach(vm.savedEntities) { fruit in
FruitRowView(vm: vm, fruit: fruit)
}
}
}
.navigationTitle("Fruits")
.sheet(isPresented: $showSheet, content: {
SecondScreen(refresh: $refresh, vm: vm)
})
}
}
}
这是我的弹出屏幕(用于创建新水果)
struct SecondScreen: View {
@Binding var refresh: Bool
@Environment(\.presentationMode) var presentationMode
@ObservedObject var vm: CoreDataViewModel
@State var textFieldText: String = ""
var body: some View {
TextField("Add fruit here...", text: $textFieldText)
.font(.headline)
.padding(.horizontal)
Button(action: {
guard !textFieldText.isEmpty else { return }
vm.addFruit(text: textFieldText)
textFieldText = ""
presentationMode.wrappedValue.dismiss()
refresh.toggle()
}, label: {
Text("Save")
})
}
}
这是我的 FruitRowView:
struct FruitRowView: View {
//instance of core data model
@ObservedObject var vm: CoreDataViewModel
var fruit: FruitEntity
@State var fruitName = fruit.name
var body: some View {
TextField("Enter fruit name", text: $fruitName)
}
}
所以我得到的错误是:
无法在属性初始值设定项中使用实例成员“fruit”;属性初始值设定项在“self”可用之前运行。
当我尝试将
FruitRowView
分配给 fruitName
时,fruit.name
中会出现此错误。我认为有一个简单的解决方法,但我一直无法弄清楚。
由于视图模型中的fruitEntities是已发布的属性,因此行中不需要状态变量。您需要行视图的绑定,并且应该在内容视图中传递它。
您也不需要将视图模型传递给行视图。
struct FruitRowView: View {
// No need to pass view model to child, only pass the data
// @ObservedObject var vm: CoreDataViewModel
@Binding var fruitName: String
// You don't need this.
// @State var fruitName = fruit.name
var body: some View {
TextField("Enter fruit name", text: $fruitName)
}
}
struct ContentView: View {
...
List {
ForEach(vm.savedEntities) { fruit in
FruitRowView(fruitName: $fruit.name)
}
}
...
}