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

问题描述:

我正在编写一个应用程序,它从HealthKit获取步骤数据并在屏幕上显示统计数据。我有一个委托协议功能的viewcontoller:setNeedsDisplay()和redraw()函数调用之间的延迟

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)") 
    } 

我发起的更新userValue通过函数:

func startRefreshByGettingUserValue() 

当功能仅仅是:

func startRefreshByGettingUserValue(){ 
     userValue = 1500; 
} 

我得到一个瞬时日志消息“DidSet”后跟userValue从redrawRect()中的值。

现在,当我改变功能:

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

查询上的匿名后台队列中运行。只要查询结果为 完成,结果处理程序就会在相同的后台队列 上执行(但不一定在同一个线程上)。您通常会将这些 结果发送到主队列以更新用户界面。

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

应该在主线程上调用UIKit API。

一个简单的办法是:

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