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

SwiftUI计数器应用中视图未同步更新但ObservableObject模型数据已正确更新的问题

Let's break down why you're seeing this real-time update issue on your physical iPhone 12 and walk through the fixes step by step:

Key Causes of the Problem

  1. Counter is likely a class instead of a struct
    If Counter is defined as a class, modifying its count property won't trigger a view update. Here's why: your CounterItems uses @Published var counterItems: [Counter], which only sends update notifications when the array's elements (their references) change or the array's length changes. Since classes are reference types, changing a property like count doesn't alter the reference stored in the array—so SwiftUI never gets the signal to refresh the view, even though the underlying data is correct.

  2. Inefficient value lookup in SingleCounterView
    You're using userData.counterItems.first(where: {$0.id == index})!.count to display the counter value. This creates a temporary optional value that SwiftUI's update system struggles to track reliably, especially under the optimizations applied on physical devices. This leads to missed refresh triggers.

  3. Filtering happens inside the ForEach loop
    You’re checking if item.isDeleted == false inside the ForEach block instead of filtering the array first. When an item's isDeleted status changes, the full counterItems array doesn't change (if Counter is a class), so ForEach doesn't know to update the view.

Step-by-Step Fixes

1. Change Counter to a struct (Critical Fix)

Switch Counter from a class to a struct. Structs are value types—modifying a property like count creates a new instance, which replaces the old one in the counterItems array. This triggers the @Published property to send an update notification, so SwiftUI refreshes the view.

Example Counter definition:

struct Counter: Identifiable {
    let id: Int
    var name: String
    var count: Int
    var step: Int
    var isDeleted: Bool
}

Making it conform to Identifiable also simplifies ForEach usage.

2. Simplify the counter value lookup

Replace the first(where:) call with direct index access—since you already have the item's id (which matches the array index), this is safe and lets SwiftUI track changes reliably:

Text("\(userData.counterItems[index].count)")
    .font(.system(size: 60))
    .frame(width: 100)

3. Filter the ForEach source upfront

Filter out deleted items before passing the array to ForEach. This ensures that any change to an item's isDeleted status updates the filtered array, triggering ForEach to refresh the view:

ForEach(userData.counterItems.filter { !$0.isDeleted }) { item in
    SingleCounterView(index: item.id)
        .environmentObject(userData)
}

4. Fix ID conflicts in initUserData (Optional but Important)

Your current initUserData regenerates IDs as output.count, which can break existing index mappings. Instead, preserve the original IDs and update the CounterItems index to avoid duplicates:

func initUserData() -> [Counter] {
    var output: [Counter] = []
    if let storedData = UserDefaults.standard.object(forKey: "counterItems") as? Data {
        let data = try! decoder.decode([Counter].self, from: storedData)
        output = data.filter { !$0.isDeleted }
    }
    // Update the starting index to avoid duplicate IDs when adding new counters
    if let maxId = output.map({ $0.id }).max() {
        CounterItems.index = maxId + 1
    } else {
        CounterItems.index = 0
    }
    return output
}

Note: You might want to make index a static property of CounterItems instead of an instance variable to avoid issues here.

Quick Verification

Your SingleCounterFullView works correctly because it likely accesses the counter value directly (not via first(where:)), which aligns with the fixes above. After applying these changes, the SingleCounterView should update in real-time on your iPhone 12 just like it does in the simulator and preview.

内容的提问来源于stack exchange,提问作者NeilShi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 23:14:06