活动指示符在异步查询结束之前停止

时间:2014-12-28 22:17:14

标签: ios

我有一个ViewController调用一个运行HealthKit查询(非常非常慢的btw)的类HKQueryWeight并将数据保存到CoreData。如果用户在查询结束前离开VC,则应用程序崩溃。

  

致命错误:在展开Optional值时意外发现nil   (LLDB)

最初,我认为我可以通过添加一个activityIndi​​cator来修补此问题,该活动开始在viewDidAppear中动画,并在VC中的最后一个函数结束时停止。有用。但是,由于我认为,由于healthKit Querys的异步性质,动画在实际的healthKit查询完成之前停止。

  • 问题:如何创建一个解决方案,只有当最后一个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)
    }
}

1 个答案:

答案 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
    }