如何让两个不同的 NS 定时器同时工作?

How do I make two different NS Timers work at the same time?

我还看到了 1 个类似的问题,但我没有提供帮助。在我的应用程序中,我希望计时器在我按下开始按钮时开始计时。我还想将一个按钮从屏幕的一侧移动到另一侧(当我单击开始按钮时)。除此之外,我希望另一个按钮在单击开始按钮延迟 1 秒后从屏幕的一侧移动到另一侧。问题是,即使我为每个计时器 (NS Timer) 指定了不同的名称,它们也会相互混淆。单击开始按钮后,用于计算秒数的计时器和移动的第一个按钮工作正常,但 1 秒后,第一个按钮回到开头并重新开始,第二个按钮开始移动但然后它做同样的事情作为第一个按钮的东西。计算秒数的计时器仍然可以正常工作。这是代码(顺便说一句,我使用 CADisplayLink 来移动按钮):

var displayLink: CADisplayLink?
var displayLink1: CADisplayLink?
@IBOutlet var moving1outlet: UIButton!
@IBOutlet var moving2outlet: UIButton!
@IBOutlet var timerlabel: UILabel!
var timer = NSTimer()
var timer2 = NSTimer()
var startTime = NSTimeInterval()
@IBAction func startbutton(sender: UIButton) {
     timer.invalidate()
    timer2.invalidate()
       //CREATING THE COUNTING TIMER
        let aSelector : Selector = "updateTime"
        timer2 = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: aSelector, userInfo: nil, repeats: true)
        startTime = NSDate.timeIntervalSinceReferenceDate()
    displayLink?.invalidate()
    displayLink1?.invalidate()
   //MOVING BUTTON 1
    moving1outlet.frame = CGRectMake(120, 400, 100, 100)
      displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
    displayLink?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
   //DELAY TO MOVE BUTTON 2
     timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "timerAction", userInfo: nil, repeats: false)  
}
func timerAction() {
    //MOVING BUTTON 2
    self.moving2outlet.frame = CGRectMake(120, 400, 100, 100)
    self.displayLink1 = CADisplayLink(target: self, selector: "handleDisplayLink1:")
    self.displayLink1?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
}
func updateTime() {
    //COUNTING TIMER
    let currentTime = NSDate.timeIntervalSinceReferenceDate()
    //Find the difference between current time and start time.
    var elapsedTime: NSTimeInterval = currentTime - startTime
    //calculate the minutes in elapsed time.
    let minutes = UInt8(elapsedTime / 60.0)
    elapsedTime -= (NSTimeInterval(minutes) * 60)
    //calculate the seconds in elapsed time.
    let seconds = UInt8(elapsedTime)
    elapsedTime -= NSTimeInterval(seconds)
    //find out the fraction of milliseconds to be displayed.
    let fraction = UInt8(elapsedTime * 100)
    //add the leading zero for minutes, seconds and millseconds and store them as string constants
    let strMinutes = String(format: "%02d", minutes)
    let strSeconds = String(format: "%02d", seconds)
    let strFraction = String(format: "%02d", fraction)
    //concatenate minuets, seconds and milliseconds as assign it to the UILabel
    timerlabel.text = "\(strMinutes):\(strSeconds).\(strFraction)"
}
func handleDisplayLink(displayLink: CADisplayLink) {
    //POSITIONING BUTTON 1
    var buttonFrame = moving1outlet.frame
    buttonFrame.origin.y += -2
    moving1outlet.frame = buttonFrame
    if moving1outlet.frame.origin.y <= 50 {
        displayLink.invalidate()
        displayLink1?.invalidate()
    }
}
func handleDisplayLink1(displayLink1: CADisplayLink) {
    //POSITIONING BUTTON 2
    var button4Frame = moving2outlet.frame
    button4Frame.origin.y += -2
    moving2outlet.frame = button4Frame
    if moving2outlet.frame.origin.y <= 50 {
        displayLink1.invalidate()
    }
}

谢谢。安东

正如我在我的 中第 1 点提到的,您正在修改 frame 值,但您无疑正在使用自动布局,因此任何触发自动布局引擎布局子视图的东西都会将视图移回约束定义的位置。所以问题不在于计时器,而是 frame 值的改变而不是约束的改变。

一个解决方案是不更改 frame 值,而是为约束创建 @IBOutlet 引用,然后在显示 link 处理程序中,更改 constant约束而不是改变 frame.

例如,如果我想在 2 秒内使用显示器 link 将视图移动 50 点的距离,我会执行如下操作。

首先,为前导约束添加一个出口:

然后我会像这样写一个显示 link 处理程序:

@IBOutlet weak var leftConstraint: NSLayoutConstraint!

var displayLink: CADisplayLink?
var startTime: CFAbsoluteTime?
let duration = 2.0

override func viewDidLoad() {
    super.viewDidLoad()

    startDisplayLink()
}

func startDisplayLink() {
    startTime = CFAbsoluteTimeGetCurrent()
    displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
    displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
}

func stopDisplayLink() {
    displayLink?.invalidate()
    displayLink = nil
}

func handleDisplayLink(displayLink: CADisplayLink) {
    let elapsed = CFAbsoluteTimeGetCurrent() - startTime!
    var percentComplete = CGFloat(elapsed / duration)

    if percentComplete >= 1.0 {
        percentComplete = 1.0
        stopDisplayLink()
    }

    leftConstraint.constant = 100.0 + 50.0 * percentComplete
}

请注意,就像我之前建议的那样,我不会在每次调用时移动固定距离(因为不同的设备可能会导致不同的速度,速度可能不恒定等),而是计算 "percent complete" 根据经过的时间,然后从中计算出坐标。