如何在通过算法不断改变视图状态时动态重新渲染视图?

时间:2021-07-15 19:26:11

标签: swift swiftui

我有一个视图,显示标记为 @State 属性的数组的值。 当例如我有一个按钮来对数组进行排序,我怎样才能实现 SwiftUI 为排序算法中的每一步更新屏幕,从而改变数组的值?

在这个测试示例中,我使用冒泡排序对数组进行排序:

struct ContentView: View {
    
    @State private var array = [5,3,6,9,1,2,4,13,3,4,16]
    
    var body: some View {
        
        HStack {
            ForEach(0..<array.count, id: \.self) { i in
                Text("\(array[i])").padding()
            }
        }
        
        Button("Sort") {
            var j = array.count
            while j >= 1 {
                for i in 1..<j {
                    if array[i] < array[i-1] {
                        let temp = array[i-1]
                        array[i-1] = array[i]
                        array[i] = temp
                    }
                }
                sleep(1) // to compensate for the short runtime in this example 
                j -= 1
            }
        }
    }
}

为什么 SwiftUI 只重新渲染视图一次,尽管 @State 属性会随着每个排序步骤而发生变化? 是否有可能以某种方式在屏幕上可视化每个步骤,而不是只看到算法的最终结果?

当运行类似于数独求解器的东西时,我希望这种行为能够可视化回溯步骤。在此先感谢您的帮助!

PS:我对 Swift/SwiftUI 很陌生,这也是我关于 Stack Overflow 的第一个问题。如果我不够具体或遗漏了一些明显的东西,请道歉!

1 个答案:

答案 0 :(得分:2)

通过使用 sleep,您会阻塞主线程并阻止 UI 更新。相反,您可以使用 DispatchQueue.main.asyncAfter 运行排序的每个连续步骤:


struct ContentView: View {
    
    @State private var array = [5,3,6,9,1,2,4,13,3,4,16]
    @State private var currentElement = 0
    
    var body: some View {
        
        HStack {
            ForEach(array, id: \.self) { i in
                Text("\(i)").padding()
            }
        }
        
        Button("Sort") {
            currentElement = array.count
            withAnimation {
                runSortStep()
            }
        }
    }
    
    func runSortStep() {
        for i in 1..<currentElement {
            if array[i] < array[i-1] {
                let temp = array[i-1]
                array[i-1] = array[i]
                array[i] = temp
            }
        }
        currentElement -= 1
        if currentElement >= 1 {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                withAnimation {
                    runSortStep()
                }
            }
        }
    }
}

请注意,我还更改了您的 ForEach,以便每个元素都由数组值而不是索引标识——否则,您将看不到动画。