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

SwiftUI中@State自定义类型数组在init()中未更新的问题

问题描述

我定义了如下结构体:

struct ValuesPerCategory: Identifiable {
    let id: UUID
    let categoryName: String
    let plan: Decimal
    let actual: Decimal
}

并声明了如下@State变量:

@State private var values: [ValuesPerCategory]

在视图结构体的init()方法中,我按如下方式初始化values变量:

init(/*some parameters*/) {
    var array = [ValuesPerCategory]()
    let objCategory = Category()
    if let categories = objCategory.getCategoriesByType(forType: categoryType, inContext: context) {
        let objValue = Value()
        for category in categories {
            let plan = objValue.getSummedValues(forCategoryID: category.id!, forValueType: .plan, forYear: year, inContext: context)
            let actual = objValue.getSummedValues(forCategoryID: category.id!, forValueType: .actual, forYear: year, inContext: context)
            array.append(ValuesPerCategory(id: category.id!, categoryName: category.name!, plan: plan, actual: actual))
        }
    }
    print(array.description)
    _values = State(initialValue: array)
}

视图首次显示时一切正常,但当其他状态变量更新导致init()被重新调用时,即使array变量内容已改变,values变量却未更新。print(array.description)的输出与视图body中的Text(values.description)显示内容不一致。若状态变量为非自定义类型数组则完全正常,请问这是什么原因?

原因分析与解决方法

核心原因

@State的设计逻辑是仅在视图首次初始化时读取初始值,后续视图重渲染(包括init被重新调用)时,SwiftUI会忽略你对_values = State(initialValue: array)的赋值,而是保留之前存储在@State外部容器中的旧值。

这是因为@State的本质是把数据存储在SwiftUI的视图外部容器中,而非视图结构体本身——视图结构体只是临时的、频繁重建的描述对象,@State变量的实际值不受视图结构体重建的影响。

你觉得自定义类型数组有问题、非自定义类型正常,只是因为非自定义类型的测试场景中,你可能用了正确的更新方式(比如直接给values赋值),而自定义类型场景下误用了init赋值,本质上@State对所有类型的行为都是一致的。

正确处理方式

  1. 不要在init中更新@State变量,init只负责首次初始化。
  2. 如果需要根据外部参数(比如categoryType、year)的变化更新values,用onChange修饰器监听参数变化,重新计算并赋值:
struct YourView: View {
    let categoryType: CategoryType
    let year: Int
    let context: NSManagedObjectContext
    @State private var values: [ValuesPerCategory] = []

    init(categoryType: CategoryType, year: Int, context: NSManagedObjectContext) {
        self.categoryType = categoryType
        self.year = year
        self.context = context
        // 仅做首次初始化
        self._values = State(initialValue: self.calculateValues())
    }

    var body: some View {
        Text(values.description)
            .onChange(of: [categoryType, year]) { _ in
                // 参数变化时重新计算更新values
                values = calculateValues()
            }
    }

    // 抽离计算逻辑为单独方法
    private func calculateValues() -> [ValuesPerCategory] {
        var array = [ValuesPerCategory]()
        let objCategory = Category()
        if let categories = objCategory.getCategoriesByType(forType: categoryType, inContext: context) {
            let objValue = Value()
            for category in categories {
                let plan = objValue.getSummedValues(forCategoryID: category.id!, forValueType: .plan, forYear: year, inContext: context)
                let actual = objValue.getSummedValues(forCategoryID: category.id!, forValueType: .actual, forYear: year, inContext: context)
                array.append(ValuesPerCategory(id: category.id!, categoryName: category.name!, plan: plan, actual: actual))
            }
        }
        print(array.description)
        return array
    }
}
  1. iOS 17+、macOS 14+可以用@DerivedState,自动根据依赖参数计算衍生值,无需手动监听:
struct YourView: View {
    let categoryType: CategoryType
    let year: Int
    let context: NSManagedObjectContext
    // 自动根据依赖计算,无需手动管理更新
    @DerivedState private var values: [ValuesPerCategory] {
        calculateValues()
    }

    var body: some View {
        Text(values.description)
    }

    private func calculateValues() -> [ValuesPerCategory] {
        var array = [ValuesPerCategory]()
        let objCategory = Category()
        if let categories = objCategory.getCategoriesByType(forType: categoryType, inContext: context) {
            let objValue = Value()
            for category in categories {
                let plan = objValue.getSummedValues(forCategoryID: category.id!, forValueType: .plan, forYear: year, inContext: context)
                let actual = objValue.getSummedValues(forCategoryID: category.id!, forValueType: .actual, forYear: year, inContext: context)
                array.append(ValuesPerCategory(id: category.id!, categoryName: category.name!, plan: plan, actual: actual))
            }
        }
        print(array.description)
        return array
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:52:49