swiftui 不会更新新状态

时间:2021-06-23 17:11:02

标签: swift swiftui

我创建了一个用户界面来控制灯光,包括一个亮度滑块。它几乎按预期工作。但是现在我添加了用于将亮度提高/降低 10% 的按钮。它的工作原理是灯光相应变暗,状态也会更新并调用 body 方法,并且正如 LightsCell 正文中的打印输出所显示的那样,使用正确的值调用它,但不会相应地更新滑块。< /p>

我错过了什么?谢谢

完整代码:https://gitlab.com/vikingosegundo/brighter-hue

video

import SwiftUI

final class ViewState: ObservableObject  {
    
    @Published var lights : [ Light ] = []
    @Published var rooms  : [ Room  ] = []

    init(store: Store) {
        store.updated { self.process(state(of: store)) }
        process(state(of: store))
    }
    
    func handle(msg: Message) { }
    
    private func process(_ appState: AppState) {
        DispatchQueue.main.async {
            self.lights = appState.lights.sorted(on:\.name, by:<)
            self.rooms = appState.rooms.sorted(on:\.title, by:<)
        }
    }
}

struct ContentView: View {
    init(viewState: ViewState, rootHandler: @escaping (Message) -> ()) {
        self.viewState = viewState
        self.rootHandler = rootHandler
    }
    
    @ObservedObject private var viewState: ViewState
    private let rootHandler: (Message) -> ()
    
    var body: some View {
        TabView {
            Dashboard(rootHandler)
            Lights   (rootHandler)
            Rooms    (rootHandler)
            Shop     (rootHandler)
            Settings (rootHandler)
        }.environmentObject(viewState)
    }
}

struct Lights: View {
    init(_ rootHandler: @escaping (Message) -> ()) {
        self.rootHandler = rootHandler
    }
    
    @EnvironmentObject
    private var viewState: ViewState
    private let rootHandler: (Message) -> ()
    
    var body: some View {
        VStack {
            NavigationView {
                List {
                    ForEach(viewState.lights) { LightCell(light: $0, rootHandler:rootHandler) }
                }.buttonStyle(PlainButtonStyle()).navigationTitle("Lights")
            }
        }.tabItem {
            Label("Lights", systemImage: "lightbulb")
        }
    }
}

struct LightCell: View {
    init(light: Light, rootHandler: @escaping (Message) -> ()) {
        self.rootHandler = rootHandler
        self.light = light
        self.isOn  = light.isOn
        self.hue   = light.hue
        self.sat   = light.saturation
        self.bri   = light.brightness
        self.ct    = Double(-light.ct)
    }
    
    var body: some View {
        print("\(light.name) \(light.id): \(light.brightness)")
        return VStack {
            HStack {
                Text("\(light.name)").bold().dynamicTypeSize(.xLarge).fixedSize()
                Spacer()
                Toggle("", isOn: $isOn)
                    .onChange(of: isOn) { _ in
                        switch (light.isOn, isOn) {
                        case (true, false): rootHandler(.lighting(.turn(light,.off)))
                        case (false, true): rootHandler(.lighting(.turn(light,.on )))
                        case (  _  ,  _  ): ()
                        }
                    }
            }
            HStack {
                Spacer()
                Picker("", selection: $_displayStyle) {
                    Image(systemName: "thermometer").tag(0)
                    Image(systemName: "paintpalette").tag(1)
                }.pickerStyle(.segmented).fixedSize()
                Spacer()
            }
            
            LazyVGrid(columns: [GridItem(.fixed(80)), GridItem(.flexible())], alignment: .trailing) {
                switch displayStyle(for: _displayStyle) {
                case .hsb:
                    Text("Hue"       ).dynamicTypeSize(.xSmall);  Slider(value: $hue) { _ in rootHandler( .lighting(.set(.values(.hsb(hue,sat,bri), on:light))) ) }
                    Text("Saturation").dynamicTypeSize(.xSmall);  Slider(value: $sat) { _ in rootHandler( .lighting(.set(.values(.hsb(hue,sat,bri), on:light))) ) }
                case .ct:
                    Text("Color Temp").dynamicTypeSize(.xSmall).minimumScaleFactor(0.5);  Slider(value: $ct, in: (-500)...(-153)) { _ in rootHandler( .lighting(.set(.values(.ct(Int(-ct),bri), on:light))) ) }
                }
                Text("Brightness").dynamicTypeSize(.xSmall); HStack {
                    Button("-")         {      rootHandler( .lighting( .decrease(.brightness, by:.percent(10), on:light )) ) }
                    Slider(value: $bri) { _ in rootHandler( .lighting( .set(.values(.bri(bri),                 on:light))) ) }
                    Button("+")         {      rootHandler( .lighting( .increase(.brightness, by:.percent(10), on:light )) ) }
                }
            }
        }
    }
    
    private let light      : Light
    private let rootHandler: (Message) -> ()
    @State private var isOn : Bool
    @State private var hue : Double
    @State private var sat : Double
    @State private var bri : Double
    @State private var ct  : Double
    
    @State private var _displayStyle: Int = 0
}

extension LightCell {
    fileprivate enum DisplayStyle: Int, Hashable {
        case hsb
        case ct
    }
    
    private func selected(_ style: DisplayStyle)  {
        _displayStyle = number(for: style)
        print("\(style) selected for \(light.name)")
    }
}

private
func displayStyle(for x:Int) -> LightCell.DisplayStyle {
    switch x {
    case 0: return .ct
    case 1: return .hsb
    default: return .ct
    }
}

private
func number(for x:LightCell.DisplayStyle) -> Int {
    switch x {
    case .ct : return 0
    case .hsb: return 1
    }
}

1 个答案:

答案 0 :(得分:1)

您还需要在点击按钮时更新 $bri var。 您需要这个,因为滑块和按钮之间没有关系/联系。

Text("Brightness"); HStack {
    Button("-") {
        rootHandler( .lighting( .decrease(.brightness, by:.percent(10), on:light )) )
        bri = light.brightness //<-- Here
    }
    Slider(value: $bri) { _ in rootHandler( .lighting( .set(.values(.bri(bri),                 on:light))) ) }
    Button("+") {
        rootHandler( .lighting( .increase(.brightness, by:.percent(10), on:light )) )
        bri = light.brightness //<-- Here
    }
}

或者你也可以使用这个

Text("Brightness"); HStack {
    // HStack Code
}
.onChange(of: light.brightness, perform: { value in
    bri = value //<-- Here
})

注意:我删除了额外的代码,因为我使用的是旧的 Xcode。