Swift: NSTimer,每次点击延迟隐藏 UIButton

Swift: NSTimer, delay hiding UIButton with each tap

我有一个按钮(实际上是两个几乎相同的按钮)可以通过另一个 UIView 中的操作取消隐藏。

第二个 UIView 中的平移使用 dispatch_after 结束后 2 秒后按钮自动隐藏,但是如果在按钮可见时点击其中一个按钮,我希望它们保持可见。这是定时器 属性 和 UIButton 子类的两个方法。我有一个在 ViewController.

中调用 "justTapped" 的@IBAction
var timer = NSTimer()

func hideAndShrink(){
    if !self.hidden && !timer.valid{
        self.hidden = true
    }
}

func justTapped(){
    timer.invalidate()
    timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "hideAndShrink", userInfo: nil, repeats: false)
}

点击任一按钮后,它们将不会隐藏。

计时器在调用选择器中的方法时是否仍然有效?

这个问题的答案,感谢luk2302,是的,定时器在发送选择器时有效

我该如何解决这个问题?

正如所建议的那样,我为计时器编写了第二种方法,该方法隐藏了没有 !timer.valid ?

的按钮

事实证明,我根本不需要第二种方法,因为我只是尝试隐藏按钮调用 justTapped。这是带有两个按钮的展开和收缩动画的最终代码。 self.direction是我希望按钮展开的方向,是自下而上,还是自上而下,同时也决定了背景图片。我认为这可能是一个枚举,但我还没有弄明白。

    func unhideAndExpand(){

    if self.hidden{
        expand(self.frame.size.height)
    }

}

func hideAndShrink(){
    if !self.hidden && !shrinking{
        shrink(self.frame.size.height)
    }
}

func justTapped(){
    timer.invalidate()
    timer = NSTimer.scheduledTimerWithTimeInterval(3.0, target: self, selector: "hideAndShrink", userInfo: nil, repeats: false)
    }

func expand(height: CGFloat){

    if self.direction == "up"{
        self.transform = CGAffineTransformMake(1, 0, 0, 1/height, 1, height/2)
    } else if self.direction == "down" {
        self.transform = CGAffineTransformMake(1, 0, 0, 1/height, 1, -height/2)
    }
    self.hidden = false
    UIView.animateWithDuration(0.5, delay: 0, options: .CurveEaseOut, animations:
        {self.transform = CGAffineTransformIdentity}, completion: nil)
}

func shrink(height: CGFloat){
    self.shrinking = true
    UIView.animateWithDuration(0.5, delay: 0, options: .CurveEaseOut, animations:
        {if self.direction == "up"{
            self.transform = CGAffineTransformMake(1, 0, 0, 1/height, 1, height/2)
        } else if self.direction == "down" {
            self.transform = CGAffineTransformMake(1, 0, 0, 1/height, 1, -height/2)
            }
        }, completion: {_ in
            self.transform = CGAffineTransformIdentity
            self.hidden = true
            self.shrinking = false})
}

感谢大家的帮助。

您确定需要!timer.valid检查吗?如果 hideAndShrink 仅由计时器调用,您似乎可以将其更改为

func hideAndShrink(){
    self.hidden = true
}

hideAndShrink 仅在计时器未失效时才会调用,因此您可以跳过此检查。

Is the timer still valid while it calls the method in the selector?

是的,是

以下是一个完整的可运行游乐场示例,演示了:

import Foundation
import XCPlayground    
XCPSetExecutionShouldContinueIndefinitely()

class Bla {
    var timer : NSTimer?

    init() {
        timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "check", userInfo: nil, repeats: false)
        
        NSTimer.scheduledTimerWithTimeInterval(1.1, target: self, selector: "checkAgain", userInfo: nil, repeats: false)
    }
    
    @objc func check(){
        print(timer!.valid)
    }
    
    @objc func checkAgain(){
        check()
    }
}

let bla = Bla()

这样会先输出true,再输出false。定时器在 触发时仍然有效。一旦您离开触发方法,计时器就会失效。

这将使您的 if 变得无用,因为该方法在计时器触发时不会执行任何操作,因为 anything && !true 的计算结果为 false

要避免该问题,您可以创建一个不同的方法 timerFired 来设置 class 的一些内部 属性,例如var timerHasFired = falsetrue。你的``然后必须检查那个变量而不是计时器:

var timer = NSTimer()
var timerHasFired = false

func hideAndShrink(){
    if !self.hidden && hideAndShrink {
        self.hidden = true
    }
}

func timerFired() {
    timerHasFired = true
    hideAndShrink()
}

func justTapped(){
    timer.invalidate()
    timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "timerFired", userInfo: nil, repeats: false)
}