Как убить NSTimer после входа в фоновый режим и создать новый таймер после того, как приложение снова станет активным?

Итак, я создаю простое приложение для iOS, используя Swift. Мне нужно убить мой NSTimer после входа приложения в фоновый режим и создать новый после того, как приложение снова станет активным.

Первоначально мое решение состоит в том, чтобы создать таймер класса NSTimer в ViewDidLoad() в основном файле контроллера. И это вызывает ошибку в моем приложении.

Думаю, мне нужно отключить таймер, используя applicationDidEnterBackground() в AppDelegate.swift. Но я не совсем уверен, как это сделать. Должен ли я создать класс таймера в AppDelegate.swift или в основном контроллере? Я не знаю, как файлы Swift разделяют классы.

Я искал в Интернете решения, но все эти сообщения слишком старые, решения написаны на Objective-C.

Я совсем новичок. Поэтому я надеюсь, что кто-то может объяснить это на Swift.

Вот код в моем основном контроллере TodoTableViewController.swift:

import UIKit
import CoreData
class TodoTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {....
...
...
override func viewDidLoad() {
    super.viewDidLoad()
    var fetchRequest = NSFetchRequest(entityName: "Todo")
    let sortDescriptor = NSSortDescriptor(key: "dayleft", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]
    if let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext {
        fetchResultController = NSFetchedResultsController(fetchRequest:fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
        fetchResultController.delegate = self
        var e: NSError?
        var result = fetchResultController.performFetch(&e)
        todos = fetchResultController.fetchedObjects as [Todo]
        if result != true {
        println(e?.localizedDescription)
        } }
    var timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector:"update", userInfo:nil, repeats: true)
}
...
}

Если я собираюсь аннулировать таймер в AppDelegate.swift, как я могу сослаться на таймер, созданный в TodoTableViewController.swift? Или, может быть, мне следует поместить весь код, связанный с таймером, в AppDelegate.swift?


обновить

Я пытался использовать NSNotificationCenter, вот мой код.

import UIKit
import CoreData

class TodoTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
...

var timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector:"viewDidLoad", userInfo:nil, repeats: true)

func update(){
    .....
}

override func viewDidLoad() {
    super.viewDidLoad()
    ...

    let notificationCenter = NSNotificationCenter.defaultCenter()

    notificationCenter.addObserver(self, selector: "didEnterBackground", name: "UIApplicationDidEnterBackgroundNotification", object: UIApplication.sharedApplication())

    notificationCenter.addObserver(self, selector: "didBecomeActive", name: "UIApplicationWillEnterForegroundNotification", object: UIApplication.sharedApplication())   
}


func didEnterBackground() {
    timer.invalidate()
}
func didBecomeActive() {
    var timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector:"update", userInfo:nil, repeats: true)
}

...
}

Поскольку таймер объявлен в didBecomeActive(), в didEnterBackground() возникает ошибка: нет члена с именем "timer". Если я объявлю timer вне didBecomeActive(), как в коде, который я разместил выше, появится сообщение об ошибке «Дополнительный аргумент« селектор »в вызове». Я уже разбираю функцию update() на селектор. Я не знаю, откуда эта ошибка.


person Antimony    schedule 13.12.2014    source источник
comment
Если вы опубликуете код, который у вас уже есть, мы сможем вам помочь. За исключением этого, все, что я могу сказать, это использовать timer.invalidate().   -  person Sean    schedule 13.12.2014
comment
Любой класс может подписаться на UIApplicationDidEnterBackgroundNotification и UIApplicationWillEnterForegroundNotification через NSNotificationCenter.   -  person Paulw11    schedule 13.12.2014
comment
@SeanDMatthews Привет, Шон, спасибо за твое предложение. Я только что опубликовал свой код. Таймер создается в ViewDidLoad(). Я не уверен, что это хороший способ сделать это.   -  person Antimony    schedule 13.12.2014
comment
@Paulw11 Привет Пол. NotificationCenter — хорошая идея. Но я ищу решение без использования уведомлений. Я попробую NSNotificationCenter позже. Спасибо!   -  person Antimony    schedule 13.12.2014
comment
В зависимости от ситуации с кодом может быть чище, чтобы AppDelegate обрабатывал недействительность таймера, поскольку этот таймер в некоторых отношениях является общим для всего приложения, а делегат приложения уже получает обратный вызов applicationDidEnterBackground(application: UIApplication) и applicationDidBecomeActive(application: UIApplication).   -  person Christian Di Lorenzo    schedule 13.12.2014
comment
@ChristianDiLorenzo Вот о чем я думаю. Как создать таймер для всего приложения? Создать его в AppDelegate.swift или TodoTableViewController.swift? Или мне следует создать еще один файл Swift timer.swift?   -  person Antimony    schedule 13.12.2014


Ответы (2)


Обновил ваш код и добавил несколько комментариев:

class TodoTableViewController: UITableViewController, NSFetchedResultsControllerDelegate 

   //better to instantiate timer inside viewDidLoad
   var timer: NSTimer!

   func update(){ }

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

      let notificationCenter = NSNotificationCenter.defaultCenter()

      //UIApplicationDidEnterBackgroundNotification & UIApplicationWillEnterForegroundNotification shouldn't be quoted
      notificationCenter.addObserver(self, selector: "didEnterBackground", name: UIApplicationDidEnterBackgroundNotification, object: nil)
      notificationCenter.addObserver(self, selector: "didBecomeActive", name: UIApplicationWillEnterForegroundNotification, object: nil)   
   }

   func didEnterBackground() {
      self.timer.invalidate()
   }

   func didBecomeActive() {
      startTimer()
   }

   func startTimer() {
      self.timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector:"update", userInfo:nil, repeats: true)
   }
}

Также обратите внимание, что NSTimer строго сохраняет target. Поэтому при закрытии viewcontroller - таймер должен быть явно недействительным. Или произойдет утечка памяти и TodoTableViewController никогда не будет уничтожен.

person Mikhail    schedule 13.12.2014

Ответ для Objective-C, но я думаю, что это очень похоже на Swift.

В ViewDidLoad добавьте два уведомления:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]];

а также

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive) name:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];

Затем добавьте это в свой класс контроллера представления.

- (void)didEnterBackground{
[_timer invalidate];
}

- (void)didBecomeActive{
_timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(update) userInfo:nil repeats:YES];
}
person Senõr Ganso    schedule 13.12.2014
comment
Таймер определен в didBecomeActive(). Ошибка в didEnterBackground(): использование неразрешенного идентификатора таймера. Как я могу сослаться на тот, который я создал в didBecomeActive()? - person Antimony; 13.12.2014
comment
Вы должны сначала объявить его @property (nonatomic) NSTimer *timer; - person Senõr Ganso; 14.12.2014