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

SwiftUI中ViewModel与View间数据传递失败问题求助

问题排查与修复

核心问题分析

  • 线程错误:Firebase的getData()在后台Task执行,更新@Published属性时未切换回主线程,SwiftUI无法感知属性变化。
  • 状态对象声明错误:View中用@ObservedObject初始化ViewModel,View重建时会重新创建实例导致数据丢失,应使用@StateObject(iOS 14+)持有唯一实例。
  • 颜色参数传递错误:直接将字符串传入Color()初始化无效,需先将字符串映射为对应Color值。

修复步骤与代码调整

1. 修复ViewModel的线程问题

在Task中更新yearOfMoods时切换到主线程:

class MoodViewModel: ObservableObject {
    @Published var yearOfMoods: MoodObject = MoodObject(year: "", jan01: "Reset", jan02: "", jan03: "", jan04: "", jan05: "", jan06: "", jan07: "", jan08: "", jan09: "", jan10: "", jan11: "", jan12: "", jan13: "", jan14: "", jan15: "", jan16: "", jan17: "", jan18: "", jan19: "", jan20: "", jan21: "", jan22: "", jan23: "", jan24: "", jan25: "", jan26: "", jan27: "", jan28: "", jan29: "", jan30: "", jan31: "")

    func loadMoods(year: String) {
        guard let user = Auth.auth().currentUser?.uid else {
            // 处理用户未登录场景
            return
        }
        let ref = Database.database().reference()
        let db = ref.child("Users").child(user).child(year).child("Moods")
        
        Task {
            do {
                let snapshot = try await db.getData()
                
                let jan01 = snapshot.childSnapshot(forPath: "jan01").value as? String ?? "Reset"
                let jan02 = snapshot.childSnapshot(forPath: "jan02").value as? String ?? "Reset"
                // 其余日期取值代码保持不变
                
                // 切换主线程更新@Published属性
                await MainActor.run {
                    self.yearOfMoods = MoodObject(year: year, jan01: jan01, jan02: jan02, jan03: jan03, jan04: jan04, jan05: jan05, jan06: jan06, jan07: jan07, jan08: jan08, jan09: jan09, jan10: jan10, jan11: jan11, jan12: jan12, jan13: jan13, jan14: jan14, jan15: jan15, jan16: jan16, jan17: jan17, jan18: jan18, jan19: jan19, jan20: jan20, jan21: jan21, jan22: jan22, jan23: jan23, jan24: jan24, jan25: jan25, jan26: jan26, jan27: jan27, jan28: jan28, jan29: jan29, jan30: jan30, jan31: jan31)
                }
            } catch {
                print("数据加载失败: \(error.localizedDescription)")
            }
        }
    }
}

2. 修复View的ViewModel声明与颜色传递

将@ObservedObject改为@StateObject,新增字符串转Color的辅助逻辑:

struct Mood: View {
    var year: String = ""
    // 使用@StateObject确保ViewModel实例唯一
    @StateObject var viewModel = MoodViewModel()
    
    // 字符串转Color的映射逻辑
    private func moodColor(from string: String) -> Color {
        switch string {
        case "Red": return .red
        case "Blue": return .blue
        case "Green": return .green
        // 根据需求扩展更多颜色映射,默认用灰色表示"Reset"
        default: return .gray
        }
    }
    
    var body: some View {
        ScrollView{
            ScrollView(.horizontal){
                HStack{
                    VStack{
                        // 日期数字部分代码保持不变
                        Text(" ")
                            .frame(width: 30, height: 30)
                        Text("1")
                            .frame(width: 30, height: 30)
                        Text("2")
                            .frame(width: 30, height: 30)
                        // ... 其余日期数字代码
                    }
                    
                    VStack{
                        Text("J")
                            .frame(width: 30, height: 30, alignment: .center)
                        // 调用辅助函数传递正确Color
                        DayIcon(moodColor: moodColor(from: viewModel.yearOfMoods.jan01), dayOfMonth: "jan01", year: year)
                        DayIcon(moodColor: moodColor(from: viewModel.yearOfMoods.jan02), dayOfMonth: "jan02", year: year)
                    }
                }
            }
        }
        .padding(15)
        .task {
            viewModel.loadMoods(year: year)
        }
    }
}

3. 额外优化建议:简化MoodObject结构

当前用大量单独的janXX属性,可改为字典减少重复代码:

struct MoodObject {
    let year: String
    var dailyMoods: [String: String] // key为"jan01"等,value为颜色字符串
    
    // 兼容原有初始化逻辑
    init(year: String, jan01: String = "Reset", jan02: String = "", jan03: String = "", jan04: String = "", jan05: String = "", jan06: String = "", jan07: String = "", jan08: String = "", jan09: String = "", jan10: String = "", jan11: String = "", jan12: String = "", jan13: String = "", jan14: String = "", jan15: String = "", jan16: String = "", jan17: String = "", jan18: String = "", jan19: String = "", jan20: String = "", jan21: String = "", jan22: String = "", jan23: String = "", jan24: String = "", jan25: String = "", jan26: String = "", jan27: String = "", jan28: String = "", jan29: String = "", jan30: String = "", jan31: String = "") {
        self.year = year
        self.dailyMoods = [
            "jan01": jan01,
            "jan02": jan02,
            "jan03": jan03,
            // ... 其余日期映射
        ]
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 07:47:01