如何从列表元素更新全局状态?
How to update global state from a list element?
我正在构建一个电影跟踪应用程序,我想从电影的详细信息页面更改电影的观看状态。有一个电影列表。在按下一个元素时,我将电影对象传递给详细视图并尝试更改其中的观看状态。
MovieListView.swift 文件:
struct MovieListView: View {
@EnvironmentObject var movies: Movies
var body: some View {
List(movies.allMovies, id: \.title) { movie in
NavigationLink(
destination: MovieView(movie: movie)) {
MovieItem(movie: movie)
}
}.navigationTitle("Movie List")
}
}
MovieItem 是列表项组件,MovieView 是电影的详细信息屏幕。
MovieView.swift 文件
struct MovieView: View {
let movie: Movie
var body: some View {
VStack{
Text(movie.title)
Text(movie.summary)
Toggle("Watched", isOn: $movie.watched)
}
}
}
在这个例子中 isOn: $movie.watched
给出了这个错误:Cannot find '$movie' in scope
我试过绑定,但由于我传递给 MovieView 的电影对象不是状态,所以绑定不起作用。
您需要提供 @Binding
。对于项目列表,这意味着您需要一些方法来生成 @Binding
以便 child 视图可以更新原始列表。您可以在 bindingForId
函数中的以下代码中看到这一点。
class Movies : ObservableObject {
@Published var allMovies : [Movie] = [Movie(watched: false, title: "Movie 1", summary: "Summary 1"),Movie(watched: false, title: "Movie 2", summary: "Summary 2")]
func bindingForId(id: UUID) -> Binding<Movie> {
.init { () -> Movie in
self.allMovies.first(where: { [=10=].id == id }) ?? Movie(watched: false, title: "", summary: "")
} set: { (newValue) in
self.allMovies = self.allMovies.map { movie in
if movie.id == newValue.id { return newValue }
return movie
}
}
}
}
struct Movie {
var id = UUID()
var watched: Bool
var title: String
var summary : String
}
struct ContentView: View {
@ObservedObject var movies: Movies = Movies()
var body: some View {
NavigationView {
List(movies.allMovies, id: \.id) { movie in
NavigationLink(
destination: MovieView(movie: movies.bindingForId(id: movie.id))) {
Text(movie.title)
}
}.navigationTitle("Movie List")
}
}
}
struct MovieView: View {
@Binding var movie: Movie
var body: some View {
VStack{
Text(movie.title)
Text(movie.summary)
Toggle("Watched", isOn: $movie.watched)
}
}
}
请注意,我将您的 ForEach
更改为使用 id
- 如果您有两部电影共享同一个标题,使用 title
可能会很有趣。如果您不想使用 ID,则必须修改 bindingForId
函数以根据标题进行过滤。
我还包括足够的样板代码来获取它 运行 作为我的示例(添加 NavigationView
、使用 @ObservedObject
、添加示例数据等),但是 none 其中应该对原始问题的方法有影响。不过,一件重要的事情是要注意 Movie
是 struct
.
我正在构建一个电影跟踪应用程序,我想从电影的详细信息页面更改电影的观看状态。有一个电影列表。在按下一个元素时,我将电影对象传递给详细视图并尝试更改其中的观看状态。
MovieListView.swift 文件:
struct MovieListView: View {
@EnvironmentObject var movies: Movies
var body: some View {
List(movies.allMovies, id: \.title) { movie in
NavigationLink(
destination: MovieView(movie: movie)) {
MovieItem(movie: movie)
}
}.navigationTitle("Movie List")
}
}
MovieItem 是列表项组件,MovieView 是电影的详细信息屏幕。
MovieView.swift 文件
struct MovieView: View {
let movie: Movie
var body: some View {
VStack{
Text(movie.title)
Text(movie.summary)
Toggle("Watched", isOn: $movie.watched)
}
}
}
在这个例子中 isOn: $movie.watched
给出了这个错误:Cannot find '$movie' in scope
我试过绑定,但由于我传递给 MovieView 的电影对象不是状态,所以绑定不起作用。
您需要提供 @Binding
。对于项目列表,这意味着您需要一些方法来生成 @Binding
以便 child 视图可以更新原始列表。您可以在 bindingForId
函数中的以下代码中看到这一点。
class Movies : ObservableObject {
@Published var allMovies : [Movie] = [Movie(watched: false, title: "Movie 1", summary: "Summary 1"),Movie(watched: false, title: "Movie 2", summary: "Summary 2")]
func bindingForId(id: UUID) -> Binding<Movie> {
.init { () -> Movie in
self.allMovies.first(where: { [=10=].id == id }) ?? Movie(watched: false, title: "", summary: "")
} set: { (newValue) in
self.allMovies = self.allMovies.map { movie in
if movie.id == newValue.id { return newValue }
return movie
}
}
}
}
struct Movie {
var id = UUID()
var watched: Bool
var title: String
var summary : String
}
struct ContentView: View {
@ObservedObject var movies: Movies = Movies()
var body: some View {
NavigationView {
List(movies.allMovies, id: \.id) { movie in
NavigationLink(
destination: MovieView(movie: movies.bindingForId(id: movie.id))) {
Text(movie.title)
}
}.navigationTitle("Movie List")
}
}
}
struct MovieView: View {
@Binding var movie: Movie
var body: some View {
VStack{
Text(movie.title)
Text(movie.summary)
Toggle("Watched", isOn: $movie.watched)
}
}
}
请注意,我将您的 ForEach
更改为使用 id
- 如果您有两部电影共享同一个标题,使用 title
可能会很有趣。如果您不想使用 ID,则必须修改 bindingForId
函数以根据标题进行过滤。
我还包括足够的样板代码来获取它 运行 作为我的示例(添加 NavigationView
、使用 @ObservedObject
、添加示例数据等),但是 none 其中应该对原始问题的方法有影响。不过,一件重要的事情是要注意 Movie
是 struct
.