SwiftUI:如何实现ViewModel跨视图访问以支持详情页收藏功能
解决SwiftUI DetailView中无法访问ViewModel的问题
核心问题分析
你遇到的Cannot find 'vm' in scope错误有两个关键原因:
- ViewModel作用域受限:你的
ViewModel定义在ContentView的extension内部,导致DetailView无法访问这个类。 - 环境对象使用错误:
@EnvironmentObject不需要手动初始化,且你注入的位置层级不够(加到了List上,应该在根视图层级注入)。
解决方案步骤
1. 调整ViewModel的作用域
把ViewModel从ContentView的extension中移出,改为独立的全局类,让所有视图都能访问:
import Foundation import SwiftUI final class ViewModel: ObservableObject{ @Published var items = [Biase]() @Published var showingFavs = false @Published var savedItems: Set<Int> = [1, 7] var filteredItems: [Biase] { showingFavs ? items.filter { savedItems.contains($0.id) } : items } private var biasStruct: BiasData = BiasData.allBias private var db = Database() init() { self.savedItems = db.load() self.items = biasStruct.biases } func sortFavs(){ withAnimation { showingFavs.toggle() } } func contains(_ item: Biase) -> Bool { savedItems.contains(item.id) } func toggleFav(item: Biase) { if contains(item) { savedItems.remove(item.id) } else { savedItems.insert(item.id) } db.save(items: savedItems) } }
2. 方法一:使用EnvironmentObject传递ViewModel
在ContentView的根视图层级注入环境对象,确保所有子视图(包括DetailView)能获取到:
struct ContentView: View { @StateObject private var vm = ViewModel() var body: some View { NavigationStack { // iOS 16+用NavigationStack,iOS 15及以下用NavigationView List(vm.filteredItems) { item in NavigationLink(destination: DetailView(entry: item)) { Image(systemName: vm.contains(item) ? "heart.fill" : "heart") .onTapGesture { vm.toggleFav(item: item) } // 这里添加列表项的其他内容 } } } .environmentObject(vm) // 在根视图层级注入环境对象 } }
修改DetailView,通过@EnvironmentObject接收ViewModel:
struct DetailView: View { let entry: Biase @EnvironmentObject var vm: ViewModel // 仅声明类型,无需初始化 var body: some View { HStack{ Image(systemName: vm.contains(entry) ? "heart.fill" : "heart") .onTapGesture { vm.toggleFav(item: entry) } // 这里添加详情页的其他内容 } } }
方法二:直接传递ViewModel参数(更适合新手理解)
如果觉得环境对象逻辑复杂,可以直接把ViewModel作为参数传给DetailView:
// ContentView中的导航链接修改 NavigationLink(destination: DetailView(entry: item, vm: vm)) { // 列表项内容 } // DetailView定义修改 struct DetailView: View { let entry: Biase @ObservedObject var vm: ViewModel // 直接接收ViewModel参数 var body: some View { HStack{ Image(systemName: vm.contains(entry) ? "heart.fill" : "heart") .onTapGesture { vm.toggleFav(item: entry) } } } }
内容的提问来源于stack exchange,提问作者John Citrowske
相关产品推荐
相关产品推荐

