Как запустить таймер с открытым ViewController? - PullRequest
0 голосов
/ 21 января 2019

Я хочу создать таймер, который включается, пока я переключаюсь на другой 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 голосов
/ 23 января 2019

Я могу показать вам один способ. Но, как и предполагалось, он имеет собственный недостаток дизайна, и его использование зависит от точности.

   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.
     }
  }

Он будет работать до тех пор, пока не станет недействительным.

0 голосов
/ 22 января 2019

Пара мыслей:

  • eighthours -= 1 в обработчике таймера немного проблематичен, поскольку предполагается, что Timer будет срабатывать без перерыва в нужном timeInterval. Но вы должны учитывать прерывания в Timer (например, пользовательский интерфейс по какой-то причине на мгновение блокируется, пользователь полностью покидает приложение и возвращается и т. Д.).

    Мы часто переключаемся с «уменьшить счетчик при каждом вызове обработчика таймера» на «выяснить, в какое время мы хотим, чтобы таймер истек». Это отделяет «модель» (время остановки) от «вида» (частота, с которой обновляется пользовательский интерфейс). Таким образом, если вы позже решите обновить свой пользовательский интерфейс с большей частотой (например, показывать миллисекунды, а не секунды, вероятно, используя CADisplayLink вместо Timer), это не изменит модель управления приложением. И это делает ваше приложение неуязвимым для кратковременных прерываний, которые могут повлиять на таймер.

    Если вы примете этот шаблон, то вы можете передать это «время остановки», свою модель, от контроллера представления к контроллеру представления, и каждый контроллер представления может запускать и останавливать свой собственный таймер в соответствии с требованиями требуемого 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 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 :

      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()
      }
      
    • Наконец, если вы хотите обновить пользовательский интерфейс с максимально возможной частотой (например, для отображения миллисекунд), вам, вероятно, следует использовать CADisplayLink, который является специальным таймером, идеально рассчитанным для обновления пользовательского интерфейса:

      private weak var displayLink: CADisplayLink?
      
      override func viewDidAppear(_ animated: Bool) {
          super.viewDidAppear(animated)
      
          let displayLink = CADisplayLink(target: self, selector: #selector(jobtime(_:)))
          displayLink.add(to: .main, forMode: .common)
          self.displayLink = displayLink
      }
      
      override func viewDidDisappear(_ animated: Bool) {
          super.viewDidDisappear(animated)
      
          displayLink?.invalidate()
      }
      
      @objc func jobtime(_ displayLink: CADisplayLink) { ... }
      

    Но общая черта во всех этих подходах состоит в том, что (а) мы исключаем сохранение сильных ссылок, которые будут мешать контроллеру представления освобождаться, когда это необходимо; и (b) мы позволяем каждому контроллеру представления обновлять свой пользовательский интерфейс с любой частотой, которую он хочет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...