Swift • 如何从视图的初始化程序启动后台任务?

Posted

技术标签:

【中文标题】Swift • 如何从视图的初始化程序启动后台任务?【英文标题】:Swift • How can I initiate a Background Task from a View's Initializer? 【发布时间】:2021-08-06 15:47:29 【问题描述】:

我正在处理SwiftUI App 并遇到了问题。

我有一个观点(File Icon)。一个Data 对象被传递给这个视图。要创建File Icon,必须将数据转换为 PDFDocument,并且必须从第一个文档页面呈现图像。

想法:由于这是一项繁重的任务,并且有显示多个文件图标的视图(例如同时创建 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() 调用时,如果调用甚至进入“main.async”内部,您是否使用断点测试过? 你最好不要那样做,因为 View init 可以被调用很多次。改为在您可以控制的工作流的某些视图模型或管理器中执行此操作。 @Asperi 谢谢你的回答。但是,我可以在View ModelInitializer 中发起Background Task 吗?在我看来,我会遇到同样的问题吗? 【参考方案1】:

这是一个简单的演示(正如你想在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...")
    

【讨论】:

以上是关于Swift • 如何从视图的初始化程序启动后台任务?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用新的 Swift 结构化并发启动后台任务?

如果应用程序正在启动,如何停止后台任务?

如何从后台线程 Swift UI GET 请求发布更改

Firebase 应用程序在从后台激活时在启动时崩溃

swift - 如何在应用程序运行时顺利加载谷歌地图视图

后台任务未在 Swift 中重新启动