我有一个ViewController调用一个运行HealthKit查询(非常非常慢的btw)的类HKQueryWeight并将数据保存到CoreData。如果用户在查询结束前离开VC,则应用程序崩溃。
致命错误:在展开Optional值时意外发现nil (LLDB)
最初,我认为我可以通过添加一个activityIndicator来修补此问题,该活动开始在viewDidAppear中动画,并在VC中的最后一个函数结束时停止。有用。但是,由于我认为,由于healthKit Querys的异步性质,动画在实际的healthKit查询完成之前停止。
我不确定是否有必要提供代码,但我已经这样做了以防它有用
的ViewController :
class ViewController: UIViewController {
@IBOutlet var activityIndicator: UIActivityIndicatorView!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
activityIndicator.startAnimating()
setupArrays ()
}
func setupArrays (){
println("setting up arrays")
if NSUserDefaults.standardUserDefaults().boolForKey("hrSwitch") == true {
var hkQueryHeartRate = HKQueryHeartRate()
hkQueryHeartRate.performHKQuery()
}
if NSUserDefaults.standardUserDefaults().boolForKey("weightSwitch") == true {
var hkQueryWeight = HKQueryWeight()
hkQueryWeight.performHKQuery()
}
self.activityIndicator.stopAnimating()
}
HKQuery
import Foundation
import CoreData
import HealthKit
class HKQueryWeight: HKQueryProtocol {
func performHKQuery() {
var appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext!
let healthKitManager = HealthKitManager.sharedInstance
let calendar = NSCalendar.currentCalendar()
let interval = NSDateComponents()
interval.day = 1
// Set the anchor date to Monday at 3:00 a.m.
let anchorComponents =
calendar.components(.CalendarUnitDay | .CalendarUnitMonth |
.CalendarUnitYear | .CalendarUnitWeekday, fromDate: NSDate())
let offset = (7 + anchorComponents.weekday - 2) % 7
anchorComponents.day -= offset
anchorComponents.hour = 3
//let now = NSDate()
let anchorDate = calendar.dateFromComponents(anchorComponents)
let quantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)
// Create the query
let query = HKStatisticsCollectionQuery(quantityType: quantityType,
quantitySamplePredicate: nil,
options: .DiscreteAverage,
anchorDate: anchorDate,
intervalComponents: interval)
// Set the results handler
query.initialResultsHandler = {
query, results, error in
if error != nil {
// Perform proper error handling here
println("*** An error occurred while calculating the statistics: \(error.localizedDescription) ***")
abort()
}
let endDate = NSDate()
let startDate =
calendar.dateByAddingUnit(.MonthCalendarUnit,
value: -6, toDate: endDate, options: nil)
// Plot the weekly step counts over the past 6 months
results.enumerateStatisticsFromDate(startDate, toDate: endDate) {
statistics, stop in
if let quantity = statistics.averageQuantity() {
let date = statistics.startDate
let weight = quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo))
println("weight date: \(date)")
println("weight value: \(weight)")
var weightData = NSEntityDescription.insertNewObjectForEntityForName("HKWeight", inManagedObjectContext: context) as HKWeight
//Saving to CoreData
weightData.setValue(weight, forKey: "weight_data")
weightData.setValue(date, forKey: "weight_date")
context.save(nil)
}
}
}
healthKitManager.healthStore.executeQuery(query)
}
}
答案 0 :(得分:3)
现在,在调用查询后立即调用self.activityIndicator.stopAnimating()
。由于查询是异步的,因此它们在被调用后仍可在后台执行一段时间,因此如果在调用查询后立即删除活动指示符,则查询可能无法完成然而。如果您希望活动指示器在完成查询后停止动画,则必须要求它停止您的异步查询块中的动画。
由于您的查询位于不同的类中,您可以发布通知以在每个查询结束时结束活动指示符的动画,然后在第二个查询完成后停止动画UIActivityIndicatorView
并且收到第二个通知,例如:
var notificationCount:Int = 0
var totalQueries = 0
func setupArrays (){
println("setting up arrays")
notificationCount = 0
totalQueries = 0
NSNotificationCenter.defaultCenter().addObserver(self, selector: "removeActivityIndicator", name:"ActivityIndicatorNotification", object: nil)
if NSUserDefaults.standardUserDefaults().boolForKey("hrSwitch") == true {
totalQueries = totalQueries + 1
var hkQueryHeartRate = HKQueryHeartRate()
hkQueryHeartRate.performHKQuery()
}
if NSUserDefaults.standardUserDefaults().boolForKey("weightSwitch") == true {
totalQueries = totalQueries + 1
var hkQueryWeight = HKQueryWeight()
hkQueryWeight.performHKQuery()
}
if totalQueries == 0 {
self.activityIndicator.stopAnimating()
}
}
func removeActivityIndicator () {
notificationCount = notificationCount + 1
if notificationCount == totalQueries {
dispatch_async(dispatch_get_main_queue()) {
self.activityIndicator.stopAnimating()
NSNotificationCenter.defaultCenter().removeObserver(self, name:"ActivityIndicatorNotification", object:nil)
}
}
}
然后在HKQueryWeight:
func performHKQuery() {
// ...All the code before the query...
// Set the results handler
query.initialResultsHandler = {
query, results, error in
//...All the code currently within your query...
NSNotificationCenter.defaultCenter().postNotificationName("ActivityIndicatorNotification", object: nil) // <-- post notification to stop animating the activity indicator once the query's complete
}