2个定时器同时

时间:2015-10-14 19:09:45

标签: ios multithreading swift timer swift2

我需要同时使用2个计时器:一个用于会话,一个用于练习。

根据练习,练习者可以是计时器或倒计时。

我将这个课程用于2个计时器,但我有1秒的延迟:我认为这是一个线程问题。我试图派遣但不工作。

此外,该类不准确,因为根据可用资源,刷新可能需要比指定时间更长的时间。

你有解决方案来解决我的问题:有2个准确的计时器,可以是计时器还是倒计时?

感谢您的帮助

public class Chronometer: NSObject {

//
// MARK: - Private Properties

private var startTime = NSTimeInterval(0)
private var accumulatedTime = NSTimeInterval(0)
private var elapsedSinceLastRefresh = NSTimeInterval(0)
private var timer = NSTimer()

//
// MARK: - Public Properties

public var elapsedTime: NSTimeInterval {
    return elapsedSinceLastRefresh + accumulatedTime
}

/// The Time Interval to refresh the chronometer. The default is 1 second
public var refreshInterval = NSTimeInterval(1)

/// Determines a time limit for this Chronometer
public var timeLimit: NSTimeInterval?

/// Optional Block that gets called on each refresh of the specified time interval.
/// If this class needs to update UI, make sure that the UI class are made in the
///  main thread, or dispatched into it.
public var updateBlock: ((NSTimeInterval, NSTimeInterval?) -> ())?

/// Optional Block that gets called when the chronometer reach its limit time
public var completedBlock: (() -> ())?

//
// MARK: - Initializers

///
/// A convenience initializer that allow specifying the refresh interval
/// :param: refreshInterval The desired refresh interval
///
public convenience init(refreshInterval: NSTimeInterval) {
    self.init()
    self.refreshInterval = refreshInterval
}

///
/// A convenience initializer that allow specifying the refesh interval and update block
/// :param: refreshInterval The desired refresh interval
/// :param: updateBlock The update block to be called on each refresh
///
public convenience init(refreshInterval: NSTimeInterval, updateBlock: (NSTimeInterval, NSTimeInterval?) -> ()) {
    self.init()
    self.refreshInterval = refreshInterval
    self.updateBlock = updateBlock
}

//
// MARK: - Internal Methods

///
/// Refresh the timer, calling the update block to notify the tracker about the new value.
///
func refreshTime() {
    // Calculate the new time
    var refreshTime = NSDate.timeIntervalSinceReferenceDate()
    self.elapsedSinceLastRefresh = (refreshTime - startTime)

    // Calculates the remaining time if applicable
    var remainingTime: NSTimeInterval? = nil
    if self.timeLimit != nil {
        remainingTime = self.timeLimit! - elapsedTime
    }

    // If an update block is specified, then call it
    self.updateBlock?(elapsedTime, remainingTime)

    // If the chronometer is complete, then call the block and
    if let limit = self.timeLimit {
        if self.elapsedTime >= limit {
            self.stop()
            self.completedBlock?()
        }
    }
}

//
// MARK: - Public Methods

///
/// Set a time limit for this chronometer
///
public func setLimit(timeLimit: NSTimeInterval, withCompletionBlock completedBlock: () -> ()) {
    self.timeLimit = timeLimit
    self.completedBlock = completedBlock
}

///
/// Start the execution of the Cronometer.
/// Start will take place using accumulated time from the last session. 
/// If the Cronometer is running the call will be ignored.
///
public func start() {
    if !timer.valid {
        // Create a new timer
        timer = NSTimer.scheduledTimerWithTimeInterval(self.refreshInterval,
            target: self,
            selector: "refreshTime",
            userInfo: nil,
            repeats: true)

        // Set the base date
        startTime = NSDate.timeIntervalSinceReferenceDate()
    }
}

///
/// Stops the execution of the Cronometer.
/// Keeps the accumulated value, in order to allow pausing of the chronometer, and
///  to keep "elapsedTime" property value available for the user to keep track.
///
public func stop() {
    timer.invalidate()
    accumulatedTime = elapsedTime
    elapsedSinceLastRefresh = 0
}

///
/// Resets the Cronometer.
/// This method will stop chronometer if it's running. This is necessary since 
///  the class is not thread safe.
///
public func reset() {
    timer.invalidate()
    elapsedSinceLastRefresh = 0
    accumulatedTime = 0
}
}

在我的UIViewController中,在viewDidLoad()中:

dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { [unowned self] in
        self.chronometerWorkout = Chronometer(refreshInterval: NSTimeInterval(0.01)) {
            (elapsedTime: NSTimeInterval, remainingTime: NSTimeInterval?) in

            dispatch_async(dispatch_get_main_queue()) {
                if let r = remainingTime {
                    self.counterView?.chronoWorkoutLabel.text = r.getFormattedInterval(miliseconds: false)
                } else {
                    self.secondsChronoWorkout = elapsedTime
                    self.counterView?.chronoWorkoutLabel.text = elapsedTime.getFormattedInterval(miliseconds: false)

                }
            }

        }
    }



    self.chronometerExo = Chronometer(refreshInterval: NSTimeInterval(0.01)) {
        (elapsedTime: NSTimeInterval, remainingTime: NSTimeInterval?) in
        if let r = remainingTime {
            self.counterView?.chronoExoLabel.text = r.getFormattedInterval(miliseconds: false)
            self.graphicView.yValues[self.selectedRow] = Double(remainingTime!)
            self.counterView?.circularTimerProgressView.progress = CGFloat(remainingTime!)

        } else {
            self.secondsChronoExo = elapsedTime
            self.counterView?.chronoExoLabel.text = elapsedTime.getFormattedInterval(miliseconds: false)
            self.graphicView.yValues[self.selectedRow] = Double(elapsedTime)
            self.counterView?.circularTimerProgressView.progress = CGFloat(elapsedTime)

        }
    }

更新:

var startWorkOutDate:CFAbsoluteTime!
var startExoTime:CFAbsoluteTime!
var timeReference:Double!


func updateTimerLabels() {

    let elapsedTimeWorkOut = CFAbsoluteTimeGetCurrent() - startWorkOutDate
    self.counterView?.chronoWorkoutLabel.text = String(format: "%.2f",elapsedTimeWorkOut)

    let startExoTime = self.startExoTime ?? 0.0
    let elapsedTimeExo = CFAbsoluteTimeGetCurrent() - startExoTime

    if timeReference != 0 {

        if elapsedTimeExo <= timeReference {
            let remainingTimeExo = timeReference - elapsedTimeExo
            self.counterView?.chronoExoLabel.text = String(format: "%.2f",remainingTimeExo)
        }

    } 

}


func startTimers(indexPath: NSIndexPath){      

    ...

    if self.startWorkoutChronometer == false {
        startWorkOutDate = CFAbsoluteTimeGetCurrent()

        self.startWorkoutChronometer = true
        self.startExoChronometer = true

        _ = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("updateTimerLabels"), userInfo: nil, repeats: true)

    }

    if self.startExoChronometer == true {
        if timeReference != 0 {
            if self.graphicView.yValues[selectedRow] == timeReference {
                startExoTime = CFAbsoluteTimeGetCurrent()

            } else {
                timeReference = Double(Int(self.graphicView.yValues[selectedRow]))
            }   
        }   
    }
}

1 个答案:

答案 0 :(得分:0)

考虑每个计时器不使用1 NSTimer个实例。使用NSTimer或甚至可能显示链接'仅触发对UI的更新。

对于您的计时器,只需将开始时间记录为NSDate和计时器类型。提供一种询问当前时间或显示值的方法。然后,计时器可以根据当前设备时间在运行中计算出来。

通过这种方式,您将无法滑动或偏移,您可以根据需要更新UI。