使用场景
在SwiftUI中,我想要实现点击“下载”按钮后,下载图片并显示下载完成,等待2秒钟后恢复“下载”文字。

我先考虑的解决方案有两种:
1、设置一个Set数组,存储每一个图片的下载状态,如果点击下载后,图片状态改为true,等待2秒后再修改为false.
2、在Button变量内创建一个局部变量,点击下载按钮,局部变量改为true,等待2秒后修改为false。
但这两种方案经过思考后,都不是非常合适。
解决方案
1、修改图片模型CustomImages
为每张图片的“下载按钮状态”设置一个单独变量:
class CustomImages: ObservableObject, Identifiable {
let id: UUID
let image: NSImage
@Published var isDownloaded: Bool = false // 新增字段
}
2、修改下载按钮逻辑
Button(action: {
DownloadImage() // 下载图片的方法
item.isDownloaded = true
// 延时 2 秒后恢复
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
item.isDownloaded = false
}
}) {
if item.isDownloaded {
Image(systemName:"checkmark")
.foregroundColor(.green)
.padding(.vertical, 5)
.padding(.horizontal, 20)
.background(Color(hex: "DFF0D8"))
.cornerRadius(20)
} else {
Text("Download")
.foregroundColor(Color(hex: "3679F6"))
.padding(.vertical, 5)
.padding(.horizontal, 20)
.background(Color(hex: "EEEEEE"))
.cornerRadius(20)
}
}

总结
在模型中新增下载状态字段,可以实现状态跟随对象,绑定更简单,容易维护。
扩展知识
按钮视图没有变化
如果按钮视图没有根据isDownloaded变量切换按钮的显示,可能遇到的是SwiftUI的限制:
如果使用的是 @Published,那么只负责追踪变量本身的变化,而不会深入追踪数组内部每个元素的变化(哪怕这些元素是 ObservableObject)。
class AppStorage:ObservableObject {
static var shared = AppStorage()
// 存储图片
@Published var images:[CustomImages] = [] // 图片数组
}
所以即使 CustomImages 是 class、在里面加了 @Published var isDownloaded = false,也不等于 @Published var images 能感知这个变化。
这意味着:
images.append(…) 会触发刷新(数组结构变化)。
images.remove(…) 会触发刷新(结构变化)。
images[0].isDownloaded = true 不会刷新(结构没变,SwiftUI不会重新渲染)。
因此,就需要创建按钮的子视图,并绑定具体的实例:
import SwiftUI
struct ImageRowView: View {
@ObservedObject var item: CustomImages
var completion: () -> Void
var body: some View {
Button(action: {
completion()
print("isDownloaded状态改为true")
item.isDownloaded = true
// 延时 2 秒后恢复
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
print("isDownloaded状态改为false")
item.isDownloaded = false
}
}) {
if item.isDownloaded {
Image(systemName:"checkmark")
} else {
Text("Download")
}
}
}
}
在SwiftUI中,绑定按钮子视图:
// 下载按钮
ImageRowView(item: item) {
saveToDownloads(file: item)
}
这样 SwiftUI 才能正确追踪这个 item 的 @Published 属性。