Swift • 如何从视图的初始值设定项启动后台任务?



我正在处理一个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...")
}
}

最新更新