【发布时间】:2021-12-24 15:38:35
【问题描述】:
我认为我在理解 @State 的确切含义方面存在差距,尤其是在显示来自 ForEach 循环的内容时。
我的场景:我创建了最小的可重现示例。下面是带有ForEach 循环的父视图。每个子视图都有一个NavigationLink。
// Parent code which passes a Course instance down to the child view - i.e. CourseView
struct ContentView: View {
@StateObject private var viewModel: ViewModel = .init()
var body: some View {
NavigationView {
VStack {
ForEach(viewModel.courses) { course in
NavigationLink(course.name + " by " + course.instructor) {
CourseView(course: course, viewModel: viewModel)
}
}
}
}
}
}
class ViewModel: ObservableObject {
@Published var courses: [Course] = [
Course(name: "CS101", instructor: "John"),
Course(name: "NS404", instructor: "Daisy")
]
}
struct Course: Identifiable {
var id: String = UUID().uuidString
var name: String
var instructor: String
}
实际困境:我为CourseView 尝试了两种变体,一种使用let 常量,另一种使用@State var 用于course 字段。下面代码中的其他 cmets。
当导航链接打开时,带有let 常量的那个成功地更新了子视图。但是,@State var 不会更新视图。
struct CourseView: View {
// Case 1: Using let constant (works as expected)
let course: Course
// Case 2: Using @State var (doesn't update the UI)
// @State var course: Course
@ObservedObject var viewModel: ViewModel
var body: some View {
VStack {
Text("\(course.name) by \(course.instructor)")
Button("Edit Instructor", action: editInstructor)
}
}
// Case 1: It works and UI gets updated
// Case 2: Doesn't work as is.
// I've to directly update the @State var instead of updating the clone -
// which sometimes doesn't update the var in my actual project
// (that I'm trying to reproduce). It definitely works here though.
private func editInstructor() {
let instructor = course.instructor == "Bob" ? "John" : "Bob"
var course = course
course.instructor = instructor
save(course)
}
// Simulating a database save, akin to something like GRDB
// Here, I'm just updating the array to see if ForEach picks up the changes
private func save(_ courseToSave: Course) {
guard let index = viewModel.courses.firstIndex(where: { $0.id == course.id }) else {
return
}
viewModel.courses[index] = courseToSave
}
}
我正在寻找的是需要循环遍历模型数组并且模型从子视图中在 DB 中更新的场景的最佳实践。
【问题讨论】:
-
您只是在使用 var 进行初始化,这就是问题所在,让您推送视图以进行更新,您可以使用 let 或使用绑定!取决于您的孩子是否会更新该值。
-
你应该使用@Binding
-
@swiftPunk 帮助我理解。因此,当父数组更新时,
let版本将获取新值。但是为什么@State不这样做呢?那里有点困惑。 -
@JoakimDanielson 在子视图中确实发生了很多变化,而父视图并没有更新那么多。我倾向于让方法。
-
@SiddharthKamaria:如果你在苹果视频中注意到
State他们都使用private这意味着任何用State包裹的值它是一种独立且自动的,只对属于的视图负责,有时我们可以初始化 State 值但我们只是初始化它!之后,如果我们尝试重新初始化,它不会被初始化,因为它已经被初始化了!除非我们杀死视图并重新初始化
标签: ios swift swiftui swiftui-navigationlink