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对所有类型的行为都是一致的。
正确处理方式
- 不要在
init中更新@State变量,init只负责首次初始化。 - 如果需要根据外部参数(比如
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 } }
- 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
相关产品推荐
相关产品推荐

