【问题标题】:SwiftUI view not updating with data changes?SwiftUI 视图不随数据更改而更新?
【发布时间】:2021-05-08 14:22:41
【问题描述】:

我正在尝试制作一个视图,一旦用户按下按钮,5 个圆圈(灯)将一个接一个地亮起。但是,当模型更改灯光状态时,视图不会更新以表示每个灯光状态。

查看

struct ReactionLightsView: View {
    
    @ObservedObject var viewModel: ReactionLightsViewModel
    
    
    var body: some View {
        VStack {
            VStack(spacing: 0) {
                HStack {
                    ForEach(viewModel.lights) { light in
                        Circle()
                            .foregroundColor(light.color)
                    }
                }
            } .padding()
            Button(action: viewModel.start ) {
                Text("Start")
            }
        }
    }
}

视图模型

class ReactionLightsViewModel: ObservableObject {
    
    @Published private var model: ReactionLightsModel
    
    init(){
        model = ReactionLightsModel()
    }
    
    var lights: [Light] {
        model.lights
    }

    func start() {
        model.startTest()
    }
}

型号

struct ReactionLightsModel {
    private(set) var lights: [Light] = [Light(), Light(), Light(), Light(), Light()]
    private(set) var start: UInt64?
    private(set) var stop: UInt64?
    private(set) var reactionTimeNanoseconds: UInt64?
    
    mutating func startTest() {
        print("start reaction test")
        for i in 0..<5 {
            lights[i].turnOn();
            sleep(1)
        }
        for i in 0..<5 {
            lights[i].turnOff()
        }
        start = DispatchTime.now().rawValue
        print("done with reaction test")
    }
    
    mutating func stopTest() {
        stop = DispatchTime.now().rawValue
        reactionTimeNanoseconds = (stop! - start!)
        print(reactionTimeNanoseconds)
    }
}

最初,我将灯光设置为布尔数组,通过此实现,灯光会变为红色(亮起),但只有在 5 秒过去后才会同时变为红色。但是,不是我将每个灯光都更改为自己的对象,并且视图根本不会更新。

struct Light: Identifiable {
    private(set) var isLit: Bool = false
    private(set) var color: Color = .gray
    let id = UUID()
    
    mutating func turnOn() {
        isLit = true
        color = .red
    }
    
    mutating func turnOff() {
        isLit = false
        color = .gray
    }
}

非常感谢有关如何解决此问题的任何建议以及有关如何改进代码的任何其他建议。

【问题讨论】:

  • 这一行似乎有点可疑: Button(action: viewModel.start) { 你的意思是 Button(action: viewModel.startTest) { ?
  • 也许甚至 viewModel.model.start 因为 viewModel 没有开始元素?
  • 很抱歉。复制代码时,我不小心从 viewModel 中留下了我们的 start 方法。重新编辑它。@Baglan
  • @Jelumar 那是我的错。该方法存在,但我在发帖时忘记复制它。将其重新添加。
  • 拥有一个既是已发布又是私有的属性,像这样 -> @Published private var model: ReactionLightsModel 是不是很荒谬?不应该是@Published var model: ReactionLightsModel,还是我弄错了?

标签: swift swiftui


【解决方案1】:

现在,你正试图在主线程上sleep——我怀疑系统甚至不会让你这样做。一般来说,应该避免使用sleep,因为它会暂停线程的执行,尤其是不会在 UI 线程上执行。从您的变量名称来看,您似乎正准备测试某人对所有灯亮或灭的反应时间,这也不适用于 sleep 策略,因为任何按钮按下都会被sleep 策略。我想你仍然可以在sleep 调用之后测量,但你将失去测量早期发生的任何事情的能力。

如果您删除 sleep 和关闭灯的代码,您会看到灯确实实际上已打开,因此并不是 UI 没有更新随着数据的变化。

我建议重写您的代码以使用TimerDispatchQueue asyncAfter。您需要跟踪灯的状态以及接下来应该点亮的灯。


更新,显示 Timer 示例的开头:


struct ReactionLightsModel {
    private(set) var lights: [Light] = [Light(), Light(), Light(), Light(), Light()]
    private var currentLight = 0
    private(set) var start: UInt64?
    private(set) var stop: UInt64?
    private(set) var reactionTimeNanoseconds: UInt64?
    
    mutating func turnOnNextLight() {
        lights[currentLight].turnOn()
        currentLight += 1
        if currentLight == lights.count {
            currentLight = 0
        }
    }
}

class ReactionLightsViewModel: ObservableObject {
    
    @Published private var model: ReactionLightsModel
    var timer : Timer?
    
    init(){
        model = ReactionLightsModel()
    }
    
    var lights: [Light] {
        model.lights
    }

    func start() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            self.model.turnOnNextLight()
        }
    }
}

【讨论】:

  • 我已经尝试过使用 Timer,但现在我得到了 Escaping closure captures mutating 'self' parameter 作为计时器,因为 lights[I].turnON() 行。此外,我的问题与它没有认识到 Light 中的变量正在更改这一事实有关。
  • 我做了一些测试,似乎睡眠没有影响。问题是在 startTest() 方法运行完成之前它不会改变颜色。
  • 我已经更新了 Timer 示例的开头(您需要完成逻辑)。你不能在你的 struct 模型中使用它(通常你不希望在你的模型中管理状态更改)因为像这样的变异函数不能存储对 self 的引用(因此你得到的错误)。因此,将逻辑移至您的视图模型(无论如何都应该在其中进行状态管理)。
  • 关于您的第二条评论,如果您删除了 sleep 和打开灯的调用,是的,您会看到它们同时打开,因为没有任何间隔来打开它们的调用开。
  • 我的代码中有错字,但一旦修正就可以了。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2020-10-29
  • 1970-01-01
  • 2021-06-16
  • 2020-04-15
  • 2018-07-11
  • 1970-01-01
  • 2020-03-06
  • 2020-05-02
相关资源
最近更新 更多