我有一个视图,显示标记为 @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 的第一个问题。如果我不够具体或遗漏了一些明显的东西,请道歉!
答案 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
,以便每个元素都由数组值而不是索引标识——否则,您将看不到动画。