SwiftUI中如何根据外部变量自动更新@State属性backgroundColor
你当前的问题核心在于两点:一是用@StateObject持有外部传入的ViewModel,无法正确监听其状态变化;二是backgroundColor作为独立的@State属性,仅在初始化时赋值,后续ViewModel中on状态的变更无法自动同步到它。
具体修改方案
调整ViewModel的属性包装器
把@StateObject private var homeLightViewModel: HomeLightViewModel替换为@ObservedObject var homeLightViewModel: HomeLightViewModel。@StateObject用于在视图内部创建并持有ViewModel实例,外部传入的ViewModel需要用@ObservedObject来监听其状态变化。移除独立的backgroundColor状态,改用计算属性
删除@State public var backgroundColor: Color及初始化时的赋值代码,改用计算属性动态生成背景色:private var backgroundColor: Color { homeLightViewModel.locations[locationIndex].lightDevices[deviceIndex].on ? .yellow : .clear }这样只要ViewModel中设备的
on状态变化,计算属性会自动更新,视图也会随之刷新。简化点击逻辑
删除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

