如何使用 NSTimer 进行倒计时?

Posted

技术标签:

【中文标题】如何使用 NSTimer 进行倒计时?【英文标题】:How can I make a countdown with NSTimer? 【发布时间】:2015-06-05 03:23:48 【问题描述】:

如何使用 Swift 使用 NSTimer 进行倒计时?

【问题讨论】:

【参考方案1】:

这是一个鸡蛋计时器。

import UIKit

class ViewController: UIViewController 
    let eggTimes = ["Soft": 0.1, "Medium": 2, "Hard": 3]
    
    var eggTime = 0
    
    var timer = Timer()
    
    @IBOutlet weak var label: UILabel!
    
    @IBAction func b(_ sender: UIButton) 
    
    timer.invalidate()
    
    let hardness = sender.currentTitle!
    eggTime = Int(eggTimes[hardness]! * 60)
    timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(update), userInfo: nil, repeats: true)
        
    @objc func update() 
        if (eggTime > 0) 
            print("\(eggTime) seconds")
            eggTime -= 1
            
        if (eggTime == 0)
            label.text = ("done")
        
    

【讨论】:

【参考方案2】:

你真的不应该。 Grand Central Dispatch 更可靠。

【讨论】:

【参考方案3】:
import UIKit

class ViewController: UIViewController 

    let eggTimes = ["Soft": 300, "Medium": 420, "Hard": 720]
    
    var secondsRemaining = 60

    @IBAction func hardnessSelected(_ sender: UIButton) 
        let hardness = sender.currentTitle!

        secondsRemaining = eggTimes[hardness]!

        Timer.scheduledTimer(timeInterval: 1.0, target: self, selector:
            #selector(UIMenuController.update), userInfo: nil, repeats: true)
    
    @objc func countDown() 
         if secondsRemaining > 0 
             print("\(secondsRemaining) seconds.")
            secondsRemaining -= 1
        
         
    

【讨论】:

欢迎来到 Stack Overflow。在回答具有已接受答案的旧问题(寻找绿色 ✓)以及其他答案时,请确保您的答案添加了新内容或对它们有帮助。 (您回答的原因是什么?其他答案是否不够,错误,过时,......?)虽然此代码 sn-p 可能会解决问题,但它没有解释为什么或如何回答问题。请考虑include an explanation for your code,因为这有助于提高您的帖子质量。 从这里开始,请通过tour 了解 Stack Overflow 的工作原理,并查看contribution guideline。【参考方案4】:

Swift 4.1 和 Swift 5。 updatetime 方法将在每秒后调用一次,秒数将显示在 UIlabel 上。

     var timer: Timer?
     var totalTime = 60
    
     private func startOtpTimer() 
            self.totalTime = 60
            self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
        
    
    @objc func updateTimer() 
            print(self.totalTime)
            self.lblTimer.text = self.timeFormatted(self.totalTime) // will show timer
            if totalTime != 0 
                totalTime -= 1  // decrease counter timer
             else 
                if let timer = self.timer  
                    timer.invalidate()
                    self.timer = nil
                
            
        
    func timeFormatted(_ totalSeconds: Int) -> String 
        let seconds: Int = totalSeconds % 60
        let minutes: Int = (totalSeconds / 60) % 60
        return String(format: "%02d:%02d", minutes, seconds)
    

【讨论】:

每当我们添加 self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true) 这里,timeInterval = 1.0 所以基本上要显示计时器值,它需要 1 秒以避免这种情况,比如计时器应该在火灾时立即运行,我应该做些什么解决方法?尝试使 timeInterval = 0.1 但这不是一个好主意,因为计时器函数将每 0.1 秒调用一次。【参考方案5】:

带有闭包的 Swift 5:

class ViewController: UIViewController 
    
var secondsRemaining = 30
    
@IBAction func startTimer(_ sender: UIButton) 
        
    Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true)  (Timer) in
        if self.secondsRemaining > 0 
            print ("\(self.secondsRemaining) seconds")
            self.secondsRemaining -= 1
         else 
            Timer.invalidate()
        
    
            

【讨论】:

如何使计时器失效? 我建议将 self.invalidate() 替换为 Timer.invalidate() 因为使用 self 会显示未解析的标识符 从我在这里看到的情况来看,这将导致一个保留周期(我有 95% 的把握——没有进行任何测试)。您需要弱捕获 self 以从计时器闭包中引用它。您还将名称 Timer 与大写 T 组合起来,使其看起来像一个类型,因此 Timer.invalidate() 读取为静态函数。这应该像 [weak self] (timer) in 那样进行更改,以捕获弱自我并明确timer 是计时器的实例。【参考方案6】:

供新手在 Playground 中使用,在 Swift 5、Xcode 11 中使用:

Import UIKit

var secondsRemaining = 10
    
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true)  (Timer) in
    if secondsRemaining > 0 
        print ("\(secondsRemaining) seconds")
        secondsRemaining -= 1
     else 
        Timer.invalidate()
    

【讨论】:

最初的问题是关于 NSTimer;在您的答案中解释两者之间的关系会有所帮助。 嗨@shader NSTimer 类现在改为Timer,也许我应该澄清一下【参考方案7】:

将此添加到代码的末尾... 并使用您要倒计时到的参数调用 startTimer 函数... 例如(距离现在日期的未来 2 小时)-> startTimer(for: Date().addingTimeInterval(60*60*2))

Click here to view a screenshot of iPhone Simulator of how it'll look

extension ViewController

    func startTimer(for theDate: String)
    
        let todaysDate = Date()
        let tripDate = Helper.getTripDate(forDate: theDate)
        let diffComponents = Calendar.current.dateComponents([.hour, .minute], from: Date(), to: tripDate)
        if let hours = diffComponents.hour
        
            hoursLeft = hours
        
        if let minutes = diffComponents.minute
        
            minutesLeft = minutes
        
        if tripDate > todaysDate
        
            timer = Timer.scheduledTimer(timeInterval: 1.00, target: self, selector: #selector(onTimerFires), userInfo: nil, repeats: true)
        
        else
        
            timerLabel.text = "00:00:00"
        
    
    
    @objc func onTimerFires()
    
        secondsLeft -= 1
        
        //timerLabel.text = "\(hoursLeft):\(minutesLeft):\(secondsLeft)"
        timerLabel.text = String(format: "%02d:%02d:%02d", hoursLeft, minutesLeft, secondsLeft)
        if secondsLeft <= 0 
            if minutesLeft != 0
            
                secondsLeft = 59
                minutesLeft -= 1
            
        
        
        if minutesLeft <= 0 
            if hoursLeft != 0
            
                minutesLeft = 59
                hoursLeft -= 1
            
        
        
        if(hoursLeft == 0 && minutesLeft == 0 && secondsLeft == 0)
        
            timer.invalidate()
        
        
    

【讨论】:

【参考方案8】:

Swift 5 的另一种方式。 难以与 UI 交互

我想展示一个在倒计时期间防止用户与其他 UI 元素交互的解决方案。 在 cmets 中,我解释了每一行代码的含义。

 var timeToSet = 0
 var timer: Timer?

 ...

 @IBAction func btnWasPressed(_ sender: UIButton) 

      //Setting the countdown time
      timeLeft = timeToSet
      //Disabling any previous timers.
      timer?.invalidate()
      //Initialization of the Timer with interval every 1 second with the function call.
      timer = Timer(timeInterval: 1.0, target: self, selector: #selector(countDown), userInfo: nil, repeats: true)
      //Adding Timer to the current loop
      RunLoop.current.add(timer!, forMode: .common)

  

 ...

 @objc func countDown() 

     if timeLeft > 0 
         print(timeLeft)
         timeLeft -= 1
      else 
         // Timer stopping
         timer?.invalidate()
     
 

【讨论】:

【参考方案9】:

问题一:

@IBOutlet var countDownLabel: UILabel!

var count = 10

override func viewDidLoad() 
    super.viewDidLoad()

    var timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(UIMenuController.update), userInfo: nil, repeats: true)


func update() 
    if(count > 0) 
        countDownLabel.text = String(count--)
    

问题 2:

两者都可以。 SpriteKit 是您用于场景、运动等的 SDK。Simple View Application 是项目模板。他们不应该冲突

【讨论】:

考虑使用 count-=1 而不是 C 中的 -- 运算符。我会把 String(count) 放在下面,count-=1。【参考方案10】:

这适用于现在的 swift 5.0 和新闻

var secondsRemaining = 60


Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)

@objc func updateCounter()
    if secondsRemaining > 0 
    print("\(secondsRemaining) seconds.")
    secondsRemaining -= 1
            
        

【讨论】:

【参考方案11】:

在 Swift 5.1 中这将起作用:

var counter = 30

override func viewDidLoad() 
    super.viewDidLoad()

    Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)




@objc func updateCounter() 
    //example functionality
    if counter > 0 
        print("\(counter) seconds to the end of the world")
        counter -= 1
    

【讨论】:

太棒了!为我工作:) 我只是添加了计时器失效以避免在多次调用计时器时出现问题。 我想要同样的东西,定时器应该被调用几次。你是怎么创造的?可以分享一下吗【参考方案12】:

Swift4

    @IBOutlet weak var actionButton: UIButton!
    @IBOutlet weak var timeLabel: UILabel!
    var timer:Timer?
    var timeLeft = 60

override func viewDidLoad() 
    super.viewDidLoad()
    setupTimer()


func setupTimer() 
    timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(onTimerFires), userInfo: nil, repeats: true)


@objc func onTimerFires() 
    timeLeft -= 1
    timeLabel.text = "\(timeLeft) seconds left"

    if timeLeft <= 0 
        actionButton.isEnabled = true
        actionButton.setTitle("enabled", for: .normal)
        timer?.invalidate()
        timer = nil
    


@IBAction func btnClicked(_ sender: UIButton) 
    print("API Fired")

【讨论】:

【参考方案13】:

XCode 10 与 Swift 4.2

import UIKit

class ViewController: UIViewController 

   var timer = Timer()
   var totalSecond = 10

   override func viewDidLoad() 
       super.viewDidLoad()
       startTimer()
   


   func startTimer() 
       timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
   

   @objc func updateTime() 

        print(timeFormatted(totalSecond))

        if totalSecond != 0 
           totalSecond -= 1
         else 
           endTimer()
        
    

    func endTimer() 
        timer.invalidate()
    

    func timeFormatted(_ totalSeconds: Int) -> String 
        let seconds: Int = totalSeconds % 60
        return String(format: "0:%02d", seconds)
    


【讨论】:

【参考方案14】:

斯威夫特 4

Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.updateTime), userInfo: nil, repeats: true)

更新功能

@objc func updateTime()
    debugPrint("jalan")

【讨论】:

【参考方案15】:

斯威夫特 3

private let NUMBER_COUNT_DOWN   = 3

var countDownLabel = UILabel()
var countDown = NUMBER_COUNT_DOWN
var timer:Timer?


private func countDown(time: Double)

    countDownLabel.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
    countDownLabel.font = UIFont.systemFont(ofSize: 300)
    countDownLabel.textColor = .black
    countDownLabel.center = CGPoint(x: self.view.frame.width / 2, y: self.view.frame.height / 2)

    countDownLabel.textAlignment = .center
    self.view.addSubview(countDownLabel)
    view.bringSubview(toFront: countDownLabel)

    timer = Timer.scheduledTimer(timeInterval: time, target: self, selector: #selector(updateCountDown), userInfo: nil, repeats: true)


func updateCountDown() 
    if(countDown > 0) 
        countDownLabel.text = String(countDown)
        countDown = countDown - 1
     else 
        removeCountDownLable()
    


private func removeCountDownLable() 
    countDown = NUMBER_COUNT_DOWN
    countDownLabel.text = ""
    countDownLabel.removeFromSuperview()

    timer?.invalidate()
    timer = nil

【讨论】:

【参考方案16】:

制作倒计时应用 Xcode 8.1、Swift 3

import UIKit
import Foundation

class ViewController: UIViewController, UITextFieldDelegate 

    var timerCount = 0
    var timerRunning = false

    @IBOutlet weak var timerLabel: UILabel! //ADD Label
    @IBOutlet weak var textField: UITextField! //Add TextField /Enter any number to Countdown

    override func viewDidLoad() 
        super.viewDidLoad()

        //Reset
        timerLabel.text = ""
        if timerCount == 0 
            timerRunning = false
        


       //Figure out Count method
    func Counting() 
        if timerCount > 0 
        timerLabel.text = "\(timerCount)"
            timerCount -= 1
         else 
            timerLabel.text = "GO!"

        

    

    //ADD Action Button
    @IBAction func startButton(sender: UIButton) 

        //Figure out timer
        if timerRunning == false 
         _ = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(ViewController.Counting), userInfo: nil, repeats: true)
            timerRunning = true
        

        //unwrap textField and Display result
        if let countebleNumber = Int(textField.text!) 
            timerCount = countebleNumber
            textField.text = "" //Clean Up TextField
         else 
            timerCount = 3 //Defoult Number to Countdown if TextField is nil
            textField.text = "" //Clean Up TextField
        

    

    //Dismiss keyboard
    func keyboardDismiss() 
        textField.resignFirstResponder()
    

    //ADD Gesture Recignizer to Dismiss keyboard then view tapped
    @IBAction func viewTapped(_ sender: AnyObject) 
        keyboardDismiss()
    

    //Dismiss keyboard using Return Key (Done) Button
    func textFieldShouldReturn(_ textField: UITextField) -> Bool 
        keyboardDismiss()

        return true
    


https://github.com/nikae/CountDown-

【讨论】:

【参考方案17】:

定时器的变量

var timer = 60

以 1.0 为间隔的 NSTimer

var clock = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "countdown", userInfo: nil, repeats: true)

在这里你可以减少计时器

func countdown() 
    timer--

【讨论】:

应该是 Selector("countdown") 在范围内找不到运算符'--';你的意思是'-= 1'吗?我们不应该使用“timer--”,而是使用“counter -= 1”

以上是关于如何使用 NSTimer 进行倒计时?的主要内容,如果未能解决你的问题,请参考以下文章

NSTimer 不适用于 AsyncSocket 库

对由 NSTimer 异步发送的 NSNotification 进行单元测试

无法弄清楚如何使用 NSTimer 更改视图

如何在后台线程上创建 NSTimer?

如何停止 NSTimer

如何在 iphone 中暂停和恢复 NSTimer