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

SwiftUI中如何根据外部变量自动更新@State属性backgroundColor

解决SwiftUI视图状态自动同步问题

你当前的问题核心在于两点:一是用@StateObject持有外部传入的ViewModel,无法正确监听其状态变化;二是backgroundColor作为独立的@State属性,仅在初始化时赋值,后续ViewModel中on状态的变更无法自动同步到它。

具体修改方案

  1. 调整ViewModel的属性包装器
    把@StateObject private var homeLightViewModel: HomeLightViewModel替换为@ObservedObject var homeLightViewModel: HomeLightViewModel。@StateObject用于在视图内部创建并持有ViewModel实例,外部传入的ViewModel需要用@ObservedObject来监听其状态变化。

  2. 移除独立的backgroundColor状态,改用计算属性
    删除@State public var backgroundColor: Color及初始化时的赋值代码,改用计算属性动态生成背景色:

    private var backgroundColor: Color {
        homeLightViewModel.locations[locationIndex].lightDevices[deviceIndex].on ? .yellow : .clear
    }
    

    这样只要ViewModel中设备的on状态变化,计算属性会自动更新,视图也会随之刷新。

  3. 简化点击逻辑
    删除buttonVisualToggle()方法,点击手势里只保留ViewModel的状态切换调用即可,无需手动更新颜色:

    .onTapGesture {
        homeLightViewModel.toggleLightDevice(locationIndex, deviceIndex)
    }
    

修改后的完整代码

import SwiftUI

struct LightDeviceCardView: View {
    
    @ObservedObject var homeLightViewModel: HomeLightViewModel
    private let locationIndex: Int
    private let deviceIndex: Int
    
    // 动态计算背景色
    private var backgroundColor: Color {
        homeLightViewModel.locations[locationIndex].lightDevices[deviceIndex].on ? .yellow : .clear
    }
    
    init(_ locationIndex: Int,
         _ deviceIndex: Int,
         homeLightViewModel: HomeLightViewModel) {
        self.locationIndex = locationIndex
        self.deviceIndex = deviceIndex
        self.homeLightViewModel = homeLightViewModel
    }
    
    var body: some View {
        ZStack(alignment: .topLeading) {
            Rectangle()
                .fill(backgroundColor)
                .frame(width: 110, height: 110)
                .cornerRadius(40)
                .offset(x: 30, y: 30)
                .blur(radius: 30)
            Rectangle()
                .foregroundStyle(.ultraThinMaterial)
                .frame(width: 170, height: 170)
                .cornerRadius(25)
            Text(homeLightViewModel
                .locations[locationIndex]
                .lightDevices[deviceIndex]
                .name)
                .foregroundColor(.primary)
                .font(.system(size: 22).bold())
                .multilineTextAlignment(.leading)
                .frame(minWidth: 95, maxWidth: 130)
                .padding(.leading, 15)
                .padding(.top, 15)
        }
        .contextMenu {
            lightCardContextMenuItems
        }
        .onTapGesture {
            homeLightViewModel.toggleLightDevice(locationIndex, deviceIndex)
        }
        .animation(.default, value: backgroundColor) // 统一设置颜色变化动画
    }
    
    var lightCardContextMenuItems: some View {
        Group {
            Button("Action 1", action: { })
            Button("Action 2", action: { })
            Button("Action 3", action: { })
        }
    }
}

struct LightDeviceCardView_Previews: PreviewProvider {
    static var previews: some View {
        let location = Location(locationName: "Bedroom")
        let device = LightDevice(id: 11,
                                 name: "Bedroom Light",
                                 location: location,
                                 data: "",
                                 ipAddress: "",
                                 on: true)
        let error = location.addDevice(device)
        let homeLightViewModel = HomeLightViewModel(locations: [location])
        LightDeviceCardView(0, 0, homeLightViewModel: homeLightViewModel)
    }
}

注意事项

确保HomeLightViewModel、Location、LightDevice都正确实现了ObservableObject,并且on属性被标记为@Published,这样ViewModel的状态变化才能被视图监听并触发刷新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 09:45:01