setNeedsDisplay() 和调用 redraw() 函数之间的延迟

Delay between setNeedsDisplay() and call of redraw() function

我正在编写一个从 HealthKit 获取步数数据并在屏幕上显示统计数据的应用程序。我有一个带有委托协议函数的 viewcontoller:

func userValueForHistogramView(sender: HistogramView) -> CGFloat? {
        return userValue
    }

其中 userValue 是:

var userValue : CGFloat = 1000 {
    didSet{
        println("DidSet")
        histogramView.setNeedsDisplay()
    }
}

HistogramView 上的 drawRect 函数如下所示:

override func drawRect(rect: CGRect) {
        var value = dataSource?.userValueForHistogramView(self)
        println("\(value)")
    }

我通过函数启动了一个用户值的更新:

func startRefreshByGettingUserValue()

当函数为:

func startRefreshByGettingUserValue(){
        userValue = 1500;
}

我收到一条即时日志消息 "DidSet",后跟来自 redrawRect() 的 userValue 值。

现在,当我将函数更改为:

func startRefreshByGettingUserValue(){
        let calendar = NSCalendar.currentCalendar()
        let today = NSDate()

        let components = calendar.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: today)

        let startDate = calendar.dateFromComponents(components)

        let endDate = calendar.dateByAddingUnit(.CalendarUnitDay,
            value: 1, toDate: startDate!, options: NSCalendarOptions(0))

        let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: endDate, options: .StrictStartDate)

        let sampleQuery = HKStatisticsQuery(quantityType: HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount), quantitySamplePredicate: predicate, options: .CumulativeSum)
            { (sampleQuery, results, error ) -> Void in

                if let quantity = results?.sumQuantity(){
                    self.userValue = CGFloat(quantity.doubleValueForUnit(HKUnit.countUnit()) )

                }
        }
        HKStore.executeQuery(sampleQuery)
}

我在日志中收到即时 "DidSet" 消息,但实际值在 10 秒后出现(即 drawRect 滞后)。

为什么会这样?以及如何让它不延迟地工作?

Apple Docs:

Queries run on an anonymous background queue. As soon as the query is complete, the results handler is executed on the same background queue (but not necessarily on the same thread). You typically dispatch these results to the main queue to update the user interface.

您作为完成处理程序传入的闭包异步运行,因此设置 userValue 并在后台调用 setNeedsDisplay()。这样不好。

应在主线程上调用 UIKit API。

一个简单的解决方法是:

var userValue : CGFloat = 1000 {
    didSet{
        println("DidSet")
        dispatch_async(dispatch_get_main_queue()) {
            histogramView.setNeedsDisplay()
        }
    }
}