微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何从列表元素更新全局状态?

如何解决如何从列表元素更新全局状态?

我正在构建一个电影跟踪应用,我想从电影的详细信息页面更改电影的观看状态。有电影列表。在按下某个元素时,我将影片对象传递到详细信息视图并尝试更改其中的观看状态。

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,以便子视图可以更新原始列表。您可以在 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: { $0.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、添加示例数据等),但这些都不应该与原始问题的方法。不过,一件重要的事情是要注意 Movie 是一个 struct

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。