SwiftUI:结合@Published变量在数组中使用自定义函数的问题
问题描述
需求
- 实现基于ViewModel的列表,通过自定义函数生成逐年薪资涨幅及年龄变化数据
- 用户在
ProfileFormView修改birthdate、hiredate时,列表需同步更新Age和Year group字段
遇到的问题
- 尝试将自定义生成数据的函数融入ViewModel的
userDataList数组时出现错误 - 改为将
userDataList设为计算属性后,遭遇初始化错误 - 移除计算属性上的
@Published(因@Published不支持计算属性)后,WorkerView中出现只读属性赋值的错误
解决方案
核心思路
通过监听birthdate和hiredate的变化,自动触发自定义函数更新列表数据,同时保留userDataList为可观察的存储属性,既解决响应式更新问题,又避免计算属性的只读限制。
具体实现
1. 重构WorkerViewModel
import Combine import SwiftUI // 假设Worker结构遵循Identifiable struct Worker: Identifiable { let id = UUID() var yearGroup: String var age: Int var salary: Double // 其他自定义字段 } class WorkerViewModel: ObservableObject { @Published var birthdate: Date = Date() @Published var hiredate: Date = Date() @Published var userDataList: [Worker] = [] private var cancellables = Set<AnyCancellable>() init() { // 初始化生成初始数据 refreshUserDataList() // 监听出生日期/入职日期变化,自动刷新列表 $birthdate .combineLatest($hiredate) .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main) .sink { [weak self] _, _ in self?.refreshUserDataList() } .store(in: &cancellables) } // 自定义生成逐年数据的核心函数 private func refreshUserDataList() { // 示例:生成近5年的薪资/年龄数据,可根据需求调整时间范围 userDataList = (0..<5).map { yearOffset in let targetYear = Calendar.current.component(.year, from: Date()) - yearOffset let age = calculateAge(for: birthdate, inYear: targetYear) let salary = calculateSalary(for: hiredate, inYear: targetYear) return Worker( yearGroup: "\(targetYear)年度", age: age, salary: salary ) } } // 辅助计算年龄 private func calculateAge(for birthdate: Date, inYear targetYear: Int) -> Int { let birthYear = Calendar.current.component(.year, from: birthdate) return targetYear - birthYear } // 辅助计算薪资涨幅 private func calculateSalary(for hiredate: Date, inYear targetYear: Int) -> Double { let hireYear = Calendar.current.component(.year, from: hiredate) let workingYears = max(0, targetYear - hireYear) let baseSalary = 5000.0 return baseSalary * (1.0 + Double(workingYears) * 0.12) // 示例:每年涨12% } }
2. 修正WorkerView(只读展示数据)
struct WorkerView: View { @ObservedObject var viewModel: WorkerViewModel var body: some View { List(viewModel.userDataList) { worker in VStack(alignment: .leading, spacing: 4) { Text(worker.yearGroup) .font(.headline) Text("年龄:\(worker.age)岁") Text("薪资:\(worker.salary, specifier: "%.2f")元") .foregroundColor(.secondary) } .padding(.vertical, 8) } } }
3. 保持ProfileFormView的绑定逻辑
struct ProfileFormView: View { @ObservedObject var viewModel: WorkerViewModel var body: some View { Form { DatePicker( "出生日期", selection: $viewModel.birthdate, displayedComponents: .date ) DatePicker( "入职日期", selection: $viewModel.hiredate, displayedComponents: .date ) } .navigationTitle("个人信息") } }
问题根源解析
- 自定义函数融入数组出错:直接在数组初始化中调用函数无法响应
birthdate/hiredate的变化,数据只会生成一次,不会同步更新 - 计算属性初始化错误:计算属性是动态推导的,不能在初始化阶段赋值,且
@Published不支持修饰计算属性 - 只读属性赋值错误:计算属性默认只读,视图试图对其赋值必然报错,而我们的场景中视图只需要展示数据,无需修改数据
内容的提问来源于stack exchange,提问作者weekendwarrior
相关产品推荐
相关产品推荐

