SwiftUI DispatchQueue 按钮

4
我不太理解SwiftUI和DispatchQueues的相关问题。以下是代码。
这段代码将持续刷新计数器状态变量,每秒钟一次。
// Example 1 - This works
struct TimerButtonTest : View {
    @State var counter: Int = 0
    var body: some View {        
        start()
        return VStack { Text("\(counter)") }
    }

    func start() {
        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000)) {
            self.counter += 1
        }
    }
}

这并不起作用。当按钮被按下后,计数器会在1秒后加1,但随即停止。

// Example 2 - This does not work
struct TimerButtonTest : View {
    @State var counter: Int = 0
    var body: some View {
        return Button(action: {self.start()}, label: {Text("\(counter)")})
    }

    func start() {
        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000)) {
            self.counter += 1
        }
    }
}

为什么计数器不像第一个例子那样继续递增呢?

@Paulw11 已经指出了你代码中的问题,你可以检查一下条件,如果计数器不等于0,则在返回语句之前手动调用启动函数,或者你可以有另一个状态变量来知道计数器是否已经启动,并相应地采取行动。 - Prashant Tukadiya
1个回答

6
在第一段代码中,每次评估按钮的正文时都会调用startstart(最终)会更新绑定到按钮Text的状态counter。 更新绑定状态会导致SwiftUI重新计算body var。 这将调用start,它调用asyncAfter,进程无限地重复。正如Rob在评论中指出的那样,这不是一个很好的方法-body可能随时被调用任意次数,因此不能保证您每秒只会得到一次更新。
在第二段代码中,只有在点击按钮时才调用start。 一秒钟后,counter将被更新,Text将被更新。 在再次点击按钮之前,不会发生任何其他操作。

2
请注意,您不应在 body 中调用 start。SwiftUI 可以随时请求您的 body 属性,并且可以多次请求。 - rob mayoff
我同意,那是一种不稳妥的方法。你应该创建一个 @BindableObject 外部视图,它实现计时器功能。每当可绑定对象更新计数器时,你的视图将会捕获它并刷新身体。 - kontiki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接