SwiftUI TabView滑动时更新Struct变量失败:timesViewed不更新求助
问题原因分析
你的timesViewed不更新的核心原因有两个:
- 临时数组副本导致修改失效:在
body中创建的tempList是itemViewModel.list的过滤副本,修改这个副本里的Item元素不会同步到原ViewModel的数组中,而且每次视图刷新时tempList都会重新生成,之前的修改会直接丢失。 - 索引匹配逻辑不可靠:直接用
currentIndex访问tempList的元素,当视图刷新导致tempList重新生成时,索引对应的元素可能已经变化,同时修改的是临时对象,无法持久化更新。
解决方案
1. 优化ViewModel结构
首先确保你的ItemListVM是遵循ObservableObject的类,并且添加过滤后的今日数据计算属性,以及更新timesViewed的方法:
class ItemListVM: ObservableObject { @Published var list: [Item] = [] // 过滤出今日需要显示的Item var todayItems: [Item] { let today = getTodaysDate().uppercased() return list.filter { $0.repeatOn == today } } // 标记Item为已查看,更新timesViewed func markItemAsViewed(_ targetItem: Item) { guard let index = list.firstIndex(where: { $0.id == targetItem.id }) else { return } var updatedItem = targetItem updatedItem.viewedThisItem() list[index] = updatedItem } // 提取日期格式化逻辑到ViewModel中 private func getTodaysDate() -> String { let formatter = DateFormatter() formatter.dateFormat = "E" return formatter.string(from: Date.now).uppercased() } }
2. 修改视图代码
移除body中的临时数组,直接使用ViewModel提供的todayItems,并修正onChange的更新逻辑:
struct ItemSessionView: View { @ObservedObject var itemViewModel: ItemListVM @State var currentIndex = 0 var body: some View { ZStack { GeometryReader { proxy in TabView(selection: $currentIndex) { // 直接使用ViewModel中过滤好的今日数据 ForEach(itemViewModel.todayItems) { item in ItemDetailView(item: item) } } .onChange(of: currentIndex) { newIndex in // 防止索引越界 guard newIndex >= 0, newIndex < itemViewModel.todayItems.count else { return } let currentItem = itemViewModel.todayItems[newIndex] // 调用ViewModel的方法更新原数据 itemViewModel.markItemAsViewed(currentItem) } } } } }
额外说明
- 原代码中
Group和if today == item.repeatOn的判断是多余的,因为todayItems已经完成了过滤。 - 使用
Item的id来定位原数组中的元素,比用索引更可靠,避免因数组元素顺序变化导致的错误。 - 确保在使用
ItemSessionView时,传入的itemViewModel是用@ObservedObject或@StateObject修饰的,这样ViewModel的变化才能触发视图刷新。
内容的提问来源于stack exchange,提问作者tryingtocode
相关产品推荐
相关产品推荐

