我正在处理一个SwiftUI App
,遇到了一个问题。
我有一个视图(
File Icon
(。将一个Data
对象传递到此视图。要创建File Icon
,必须将数据转换为PDF文档,并且必须从第一个文档页面呈现图像。
想法:由于这是一项繁重的任务,并且有些视图显示多个文件图标(例如,同时创建20个文件图标(,因此计划创建带有某种占位符的File Icon
,在后台队列中渲染图像,并在完成后更改显示的图像。
代码:
struct FileIcon: View {
let data: Data
@State private var image: UIImage?
init(for data: Data) {
self.data = data
renderImage() // Initiate the Background Task
}
var body: some View {
Group {
if let safeImage = image {
// Some Placeholder View
} else {
Image(uiImage: safeImage)
}
}
}
private func renderImage() {
DispatchQueue.global(qos: .userInitiated).async {
// Heavy Logic
DispatchQueue.main.async {
image = renderedImage
}
}
}
}
问题:如您所见,我正在View's Initializer
中启动Background Task
。Xcode没有给出任何错误。但是,当运行此代码时,占位符视图将继续显示,并且不会切换到实际渲染的图像。在.onAppear()
内部调用renderImage()
时,占位符会在一段时间后被渲染的图像替换。
问题:如何从View's Initializer
启动Background Task
?
这里有一个简单的演示(正如你想在init
中做的那样,但是,由于我不知道你的场景,可能最好把它移到分离的方法中并显式调用(
使用Xcode 12.5/iOS 14.5 进行测试
struct DemoBackgroundTaskView: View {
class ViewModel: ObservableObject {
@Published var finished = false
init() {
print("Init") // test called once
// set up once performed background task
DispatchQueue.global(qos: .background).async {
sleep(10) // do your long task here
DispatchQueue.main.async { [weak self] in
self?.finished = true
}
}
}
}
@StateObject private var vm = ViewModel()
var body: some View {
Text(vm.finished ? "Done" : "Loading...")
}
}