Swift中如何在不同View间共享并使用数组数据
解决SwiftUI中两个View共享数组数据的问题
你之前的做法无效的核心原因:在NewsView中创建SavedNewsView的新实例,修改的只是这个新实例的savedArticles数组,和界面上正在显示的SavedNewsView实例完全无关,自然无法同步数据。
下面是正确的实现方案,通过共享ViewModel实现两个View的数据同步:
1. 创建共享数据源ViewModel
这个ViewModel负责管理savedArticles数组,所有对数组的操作都在这里统一处理:
兼容iOS13+的写法(使用ObservableObject)
import SwiftUI import Combine class SavedArticlesViewModel: ObservableObject { // @Published会自动通知View数据更新 @Published var savedArticles: [Article] = [] // 添加文章的方法 func addArticle(_ article: Article) { savedArticles.append(article) } // 删除文章的方法 func removeArticle(at offsets: IndexSet) { savedArticles.remove(atOffsets: offsets) } }
iOS17+简化写法(使用@Observable)
import SwiftUI @Observable class SavedArticlesViewModel { var savedArticles: [Article] = [] func addArticle(_ article: Article) { savedArticles.append(article) } func removeArticle(at offsets: IndexSet) { savedArticles.remove(atOffsets: offsets) } }
2. 修改SavedNewsView,依赖共享ViewModel
把原来的@State数组替换为对ViewModel的引用:
iOS13-16版本
struct SavedNewsView: View { @ObservedObject var viewModel: SavedArticlesViewModel var body: some View { NavigationView { List { ForEach(viewModel.savedArticles, id: \.self) { article in NewsComponentView(title: article.title, description: article.description, urlToImage: article.urlToImage, url: article.url) } .onDelete(perform: viewModel.removeArticle) } .listStyle(.plain) } .navigationTitle("Saved News") } }
iOS17+版本
struct SavedNewsView: View { let viewModel: SavedArticlesViewModel var body: some View { NavigationStack { // iOS16+推荐用NavigationStack替代NavigationView List { ForEach(viewModel.savedArticles, id: \.self) { article in NewsComponentView(title: article.title, description: article.description, urlToImage: article.urlToImage, url: article.url) } .onDelete(perform: viewModel.removeArticle) } .listStyle(.plain) } .navigationTitle("Saved News") } }
3. 修改NewsView,引用同一个ViewModel
移除原来的savedArray实例,改用共享的ViewModel来添加文章:
iOS13-16版本
struct NewsView: View { @StateObject var newsViewModel = ViewModel() @ObservedObject var savedViewModel: SavedArticlesViewModel var body: some View { List { ForEach(newsViewModel.news, id: \.self) { article in if article.urlToImage != nil { NewsComponentView(title: article.title, description: article.description, urlToImage: article.urlToImage, url: article.url) .swipeActions() { Button { // 调用ViewModel的方法添加文章 savedViewModel.addArticle(article) } label: { Label("Save", systemImage: "bookmark") } .tint(.yellow) } } } } .listStyle(.plain) .onAppear { newsViewModel.fetch() } } }
iOS17+版本
struct NewsView: View { @StateObject var newsViewModel = ViewModel() let savedViewModel: SavedArticlesViewModel var body: some View { List { ForEach(newsViewModel.news, id: \.self) { article in if article.urlToImage != nil { NewsComponentView(title: article.title, description: article.description, urlToImage: article.urlToImage, url: article.url) .swipeActions() { Button { savedViewModel.addArticle(article) } label: { Label("Save", systemImage: "bookmark") } .tint(.yellow) } } } } .listStyle(.plain) .onAppear { newsViewModel.fetch() } } }
4. 在父View中初始化共享ViewModel并传递给子View
比如在TabView或其他父容器中,创建一次ViewModel实例,再传递给两个子View:
struct MainView: View { // iOS13-16用@StateObject,iOS17+用@State @StateObject var savedViewModel = SavedArticlesViewModel() // @State var savedViewModel = SavedArticlesViewModel() // iOS17+ var body: some View { TabView { NewsView(savedViewModel: savedViewModel) .tabItem { Label("News", systemImage: "newspaper") } SavedNewsView(viewModel: savedViewModel) .tabItem { Label("Saved", systemImage: "bookmark") } } } }
原理说明
共享ViewModel持有唯一的数组数据源,通过@Published(或@Observable的自动追踪)实现数据变更通知,两个View都引用同一个ViewModel实例,所以任何对数组的修改都会同步触发两个View的刷新,实现数据一致。
内容的提问来源于stack exchange,提问作者zrgn4k
相关产品推荐
相关产品推荐

