UI 在主线程上的密集函数期间未更新(在 Swift 中)

UI not updating (in Swift) during intensive function on main thread

我想知道是否有人可以就如何“强制”UI 在 Swift 中的一个特别密集的函数(在主线程上)进行更新提供建议。

解释一下:我正在尝试向我的应用程序添加“导入”功能,这将允许用户从备份文件中导入项目(可以是 1 - 1,000,000 条记录,具体取决于大小)他们的备份)保存到应用程序的 CodeData 数据库中。此函数使用“for in”循环(循环遍历备份文件中的每条记录),并且对于该循环中的每个“for”,该函数都会向委托(ViewController)发送一条消息以更新其UI带有进度的 ProgressBar,以便用户可以在屏幕上看到实时进度。我通常会尝试将这个密集函数发送到后台线程,并在主线程上单独更新 UI ……但这不是一个选项,因为在 CoreData 上下文中创建这些项目必须在主线程上完成线程(根据 Swift 的 errors/crashes,当我最初尝试在后台线程上执行此操作时),我认为这因此导致 UI“冻结”而不是实时更新在屏幕上。

代码的简化版本是:

class CoreDataManager {
    
    var delegate: ProgressProtocol?
    
    // (dummy) backup file array for purpose of this example, which could contain 100,000's of items
    let backUp = [BackUpItem]()
    
    // intensive function containing 'for in' loop
    func processBackUpAndSaveData() {
        
        let totalItems: Float = Float(backUp.count)
        var step: Float = 0
        
        for backUpItem in backUp {

            // calculate Progress and tell delegate to update the UIProgressView
            step += 1
            let calculatedProgress = step / totalItems
            delegate?.updateProgressBar(progress: calculatedProgress)
            
            // Create the item in CoreData context (which must be done on main thread)
            let savedItem = (context: context)
        }
        // loop is complete, so save the CoreData context
        try! context.save()
    }
}


// Meanwhile... in the delegate (ViewController) which updates the UIProgressView
class ViewController: UIViewController, ProgressProtocol  {
    
    let progressBar = UIProgressView()
    
    // Delegate function which updates the progress bar
    func updateProgressBar(progress: Float) {

        // Print statement, which shows up correctly in the console during the intensive task
        print("Progress being updated to \(progress)")

        // Update to the progressBar is instructed, but isn't reflected on the simulator
        progressBar.setProgress(progress, animated: false)
    }
}

需要注意的一件重要事情:上面代码中的 print 语句运行良好/符合预期,即在整个“for in”循环中(可能需要一分钟或二),控制台连续显示所有打印语句(显示递增的进度值),所以我知道委托'updateProgressBar'函数肯定正确触发,但屏幕上的进度条本身 根本没有更新/没有改变......我假设这是因为 UI 被冻结并且没有'时间'(为了想要一个更好的词)来反映给定的更新进度主函数强度 运行.

我是编码方面的新手,所以如果我要求澄清任何回复,请提前致歉,因为其中大部分内容对我来说都是新的。如果相关,我正在使用故事板(而不是 SwiftUI)。

只是真的在寻找关于是否有任何(相对简单的)途径来解决这个问题的任何建议/提示,并且基本上 'force' UI 在这个密集的任务中更新。

你说“......只是真的在寻找关于是否有任何(相对简单的)途径来解决这个问题的任何建议/提示,并且基本上 'force' UI 在这个密集期间更新任务。

没有。如果你在主线程上同步做耗时工作,你阻塞了主线程,UI更新不会生效,直到你的代码returns.

您需要弄清楚如何 运行 在后台线程上执行您的代码。我已经有一段时间没有使用 CoreData 了。我知道 可能 在后台线程上执行 CoreData 查询,但我不再记得细节了。这就是您需要做的。

关于您对打印语句的评论,这是有道理的。 Xcode 控制台与您应用的 运行 循环是分开的,即使您的代码没有 return,它也能够显示输出。然而,应用程序 UI 无法做到这一点。