如何使用打开的ViewController启动计时器?

时间:2019-01-21 18:44:59

标签: ios swift timer

我想创建一个计时器,当我切换到另一个ViewController并稍后再返回时,该计时器将继续运行。我的解决方案是这样的:

  1. 我用按钮启动计时器
  2. 然后我将整数移交给下一个ViewController
  3. 我使用新的整数启动计时器

所以我的问题出在第三步:我想在函数viewDidLoad()中启动计时器。但是计时器不会在下一个ViewController中启动。

我希望有人能帮助我。告诉我,是否有更好的方法来做我想要的事情。

这是我的代码:

var timer = Timer()
var eighthours: Int = 8
var activejob: Bool = false

override func viewDidLoad() {
    super.viewDidLoad()
    identify_activejob()
    timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(Jobs.jobtime), userInfo: nil, repeats: true)
}

//functions
@objc func jobtime() {
    eighthours -= 1
}

2 个答案:

答案 0 :(得分:1)

一些想法:

  • 计时器处理程序中的eighthours -= 1有点问题,因为它假定Timer会在不中断的情况下以所需的timeInterval触发。但是您应该在Timer中适应中断(例如,由于某种原因,UI暂时被阻塞,用户完全离开了应用并返回,等等。)

    我们经常从“每次调用计时器处理程序时递减一些计数器”转变为“弄清楚希望计时器到期的时间”。这样可以将“模型”(停止时间)与“视图”(UI更新的频率)分离。这样一来,如果您以后决定以更高的频率(例如,显示毫秒而不是秒,可能使用CADisplayLink而不是Timer来更新UI),则不会更改驱动应用的模型。并且使您的应用不受可能影响计时器的短暂中断的侵扰。

    如果采用这种模式,则可以绕过这个“停止时间”,模型从视图控制器到视图控制器,每个视图控制器都可以根据该场景所需UX的需要启动和停止自己的计时器。 。

    因此,要启动一个将在8秒内停止的计时器:

    var stopTime: Date?   // set this when you start the timer
    
    func startTimer() {
        stopTime = Date().addingTimeInterval(8)
    
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(jobtime(_:)), userInfo: nil, repeats: true)
    }
    

    计时器处理程序可以确定timeIntervalSince还剩下多少时间来计算两个日期之间的浮点差(以秒为单位)。

    @objc func jobtime(_ timer: Timer) {
        let now = Date()
    
        guard let stopTime = stopTime, now < stopTime else {
            timer.invalidate()
            return
        }
    
        let timeRemaining = stopTime.timeIntervalSince(now)
    
        ...
    }
    

    我还使用jobtime参数更新了timer,以便您一眼就能看到它是Timer处理程序。

  • 仅供参考,您的代码为视图控制器引入了强大的参考,这将阻止其被释放。基于选择器的Timer对其目标保持强烈引用,而运行循环则对Timer保持引用,因此只有在您invalidate {{ 1}}。

    有两种解决方法:

    • 随着视图的出现和消失,启动和停止计时器:

      Timer

      请注意,我们在weak var timer: Timer? override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(jobtime(_:)), userInfo: nil, repeats: true) } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) timer?.invalidate() } @objc func jobtime(_ timer: Timer) { ... } viewDidAppear(而不是viewDidDisappear)中执行此操作,以确保计时器的启动和停止始终保持平衡。

    • 另一种模式是使用基于块的viewDidLoad,使用Timer引用来避免让计时器保持对视图控制器的强引用,然后可以[weak self]使用它在invalidate方法中:

      deinit
    • 最后,如果您想以最大可能的频率(例如显示毫秒)更新UI,则可能会使用weak var timer: Timer? override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in self?.jobtime(timer) } } deinit { timer?.invalidate() } 这是一个特殊的计时器,它是UI更新的理想时机:

      CADisplayLink

    但是所有这些方法的共同特征是:(a)我们消除了持久保留的强引用,这些强引用会在适当时干扰视图控制器的释放; (b)我们让每个视图控制器以所需的任意频率更新其UI。

答案 1 :(得分:1)

我可以告诉你一种方法。但是可能会建议,它具有本机设计缺陷,并且使用它取决于准确性。

   let timer = Timer.init(timeInterval: 1, repeats: true) { (timer) in
let string = ISO8601DateFormatter().string(from: Date())
 print("running" + string)
     }
   }
    class TimerViewController: UIViewController {

     override func viewDidLoad() {
      super.viewDidLoad()
      RunLoop.main.add(timer, forMode: RunLoop.Mode.default)
    // Do any additional setup after loading the view.
     }
  }

它将一直运行直到失效。