SwiftUI视图未更新:调用含@Published属性的函数无响应
问题根源与解决方案
核心问题:重复创建ViewModel实例
你在DiscoverView里同时生成了两个完全独立的DiscoverViewModel对象:
- 用
@StateObject var viewModel = DiscoverViewModel()创建的实例,是父视图列表绑定的数据源 - 但通过
.environmentObject(DiscoverViewModel())注入给子视图的是另一个全新实例
这就导致子视图修改筛选条件时,根本没影响到父视图绑定的那个ViewModel,自然列表不会更新。
修复步骤
1. 统一ViewModel实例
删除DiscoverView中重复创建的.environmentObject(DiscoverViewModel()),替换为注入已有的viewModel实例:
struct DiscoverView: View { @EnvironmentObject var navigation: NavigationManager @StateObject var viewModel = DiscoverViewModel() var body: some View { NavigationStack(path: $navigation.discoverStack) { ScrollView { VStack { DiscoverFiltersControls() ForEach(viewModel.trendingShows) { show in Text(show.title) } } .padding(.vertical) } .scrollIndicators(.hidden) .navigationTitle("Discover") .toolbar { ProfileToolbar() } } // 注入已创建的viewModel,而非新实例 .environmentObject(viewModel) } }
2. 补全Genre筛选的触发逻辑
当前选择Genre后没有调用getTrendingShows(),需要在DiscoverFiltersControls中添加监听:
// 放在typeFilters的onChange代码块之后 .onChange(of: viewModel.selectedGenre) { viewModel.getTrendingShows() }
同时要在getTrendingShows()里加入Genre筛选的逻辑(否则选了Genre也不会过滤结果):
func getTrendingShows() { Task { let isFiltered = !self.typeFilters.isEmpty && self.typeFilters.count != ShowType.allCases.count let endpoint: TrendingEndpoint = !isFiltered ? .all : self.typeFilters.contains(.movie) ? .movies : .tvSeries var shows = try await TMDbManager.shared.trending(endpoint).toShows() // 新增Genre过滤 if let selectedGenre = self.selectedGenre { shows = shows.filter { $0.genreIds.contains(selectedGenre.id) } } self.trendingShows = shows } }
3. 确保ForEach的数据源合规
SwiftUI的ForEach要求元素遵循Identifiable协议,或者显式指定id参数。如果Show结构体还没实现Identifiable,修改ForEach代码:
ForEach(viewModel.trendingShows, id: \.id) { show in Text(show.title) }
内容的提问来源于stack exchange,提问作者Curt Orion-Indev
相关产品推荐
相关产品推荐

