首页 > 解决方案 > 当通过算法不断改变视图的状态时,如何动态地重新渲染视图?

问题描述

@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 上的第一个问题。如果我不够具体或遗漏了一些明显的东西,我深表歉意!

标签: swiftswiftui

解决方案


通过使用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,以便每个元素都由数组值而不是索引标识 - 否则,您将看不到动画。


推荐阅读