You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI TabView滑动时更新Struct变量失败:timesViewed不更新求助

问题原因分析

你的timesViewed不更新的核心原因有两个:

  1. 临时数组副本导致修改失效:在body中创建的tempList是itemViewModel.list的过滤副本,修改这个副本里的Item元素不会同步到原ViewModel的数组中,而且每次视图刷新时tempList都会重新生成,之前的修改会直接丢失。
  2. 索引匹配逻辑不可靠:直接用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 23:03:06