如何更新 NSProgressIndicator?

How to update the NSProgressIndicator?

我对 NSProgress 有疑问。问题是 NSProgressIndicator 在过程中没有更新,并且在过程结束时只显示一小部分完成的部分。 localizedDescription 也仅在流程结束时显示,但已完成 100%。

所以,我有一个 class,其中一种方法 findRepeatsWithProgressReporting 使用 NSProgress

class TestProgress: NSObject, ProgressReporting
{
    let progress: Progress

    override init()
    {
        progress = Progress()
        super.init()
    }

    func findRepeatsWithProgressReporting(stringToSearch: String, minimalLength: Int, maximalLength: Int) -> [String]
    {
        var arrayOfRepeats = [String]()

        progress.totalUnitCount = Int64((minimalLength...maximalLength).count)

        for i in minimalLength...maximalLength
        {
            let arrayOfStrings = stringToSearch.chopString(stringOut: stringToSearch, length: i)
            let arrayOfUniqueStrings = Array(Set(arrayOfStrings))

            for each in arrayOfUniqueStrings
            {
                let arrayOfNSRanges = stringToSearch.searchForNSRangesOfStringInString(stringOut: stringToSearch, stringIn: each)

                var positions = String()

                if arrayOfNSRanges.count > 1
                {
                    for each1 in arrayOfNSRanges
                    {
                        let repeatStart = String(each1.location + 1)
                        let repeatEnd = String(each1.location + each1.length)
                        positions += "(" + repeatStart + "-" +  repeatEnd + ")"
                    }
                    let stringToShow = each + " " + positions
                    arrayOfRepeats.append(stringToShow)
                }
            }
            progress.completedUnitCount += 1
        }
        return  arrayOfRepeats
    }

}

然后,在 myVewContrloler 中,我有 parentProgress repeatsProgress 具有 totalUnitCount: 10 并将方法 findRepeatsWithProgressReporting 的任务作为 childProgress 添加到 parentProgress repeatsProgress使用 repeatsProgress.becomeCurrent(withPendingUnitCount: 10)

private var progressObservationContext = 0

class myVewContrloler: NSViewController
{
    ...

    var testProgress = TestProgress ()
    var repeatsProgress = Progress() 

    @IBOutlet weak var repeatsSearchProgressBar: NSProgressIndicator!
    @IBOutlet weak var repeatsPercentText: NSTextField!

    @IBOutlet weak var minimalLength: NSTextField!
    @IBOutlet weak var maximalLength: NSTextField!
    @IBOutlet var foundRepeats: NSTextView!

    @IBAction func actionFindRepeats(_ sender: AnyObject)
    {
        repeatsProgress = Progress(totalUnitCount: 10)

        let options : NSKeyValueObservingOptions = [.new, .old, .initial, .prior]
        repeatsProgress.addObserver(self, forKeyPath: "fractionCompleted", options: options, context: &progressObservationContext)
        repeatsProgress.addObserver(self, forKeyPath: "localizedDescription", options: options, context: &progressObservationContext)

        var arrayOfRepeats = [String]()

        repeatsProgress.becomeCurrent(withPendingUnitCount: 10)
        arrayOfRepeats = testProgress.findRepeatsWithProgressReporting(stringToSearch: stringToSearch, minimalLength: minimalLength.integerValue, maximalLength: maximalLength.integerValue)

        ...

        repeatsProgress.removeObserver(self, forKeyPath: "fractionCompleted")
        repeatsProgress.removeObserver(self, forKeyPath: "localizedDescription")

        repeatsProgress.resignCurrent()
    }
}

最后一部分是针对 KVO 的:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
        guard context == &progressObservationContext else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }

    if keyPath == "fractionCompleted"
    {
        OperationQueue.main.addOperation{
                let progress = object as! Progress
                self.repeatsSearchProgressBar.doubleValue = progress.fractionCompleted
                self.repeatsPercentText.stringValue = progress.localizedDescription
        }
    }
}

我已经添加了

print("Observed Something")

里面
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{  ...

我看到的是 "Observed Something" 在开始后立即打印两次,在结束时打印六次,中间没有打印(正如更新过程所预期的那样)。可能是什么原因?

这似乎是一个并发问题。由于 func actionFindRepeats(_ sender: AnyObject) 在主线程中是 运行,它与 UI 更新同时发生,这直接影响 NSProgressIndicator

有关详细信息,请参阅该答案的最后一个示例:

您可以尝试将 actionFindRepeats 函数的所有内容添加到该块中,看看它是否有效:

DispatchQueue.global().async {
    // qos' default value is ´DispatchQoS.QoSClass.default`
}

该块的参考: