Запуск таймера с другого контроллера

xcode
ios
swift3

#1

Есть два контроллера. На первом в label отображается таймер, который можно запустить как с первого контроллера, так и со второго по нажатию на кнопку старта. На втором контроллере таймер не отображается. Как реализовать старт из первого контроллера понятно, а вот как из второго запнулся и не могу сообразить…

@IBOutlet weak var label: UILabel!
var liveTimer = Timer()
var secondsLeft: Int = 1200
var liveTimerRun = false
var minutes: Int = 0
var seconds: Int = 0

func updateTimer(_ runningTimer: Timer) {
   secondsLeft -= 1
   if secondsLeft == 0 {
         label. .text = "00:00"
         liveTimerRun = false
   } else {
         minutes = (secondsLeft % 3600) / 60
         seconds = (secondsLeft % 3600) % 60
         label.text = String(format: "%02d:%02d", minutes, seconds)
   }
}

@IBAction func startTimerInFirstController(sender: AnyObject) {
        if liveTimerRun == false {
            liveTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(FirstController.updateTimer), userInfo: nil, repeats: true)
            liveTimerRun = true
        }
}

При добавлении старта таймера в экшин стартовой кнопки на втором контроллере, приложение падает, это и логично. Ссылка селектора обозначает первый контроллер. А активируем на втором, поменяв в записи на второй контроллер, вылезит ошибка в updateTimer и тд

@IBAction func startTimerInSecondController(sender: AnyObject) {
        if liveTimerRun == false {
            liveTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(FirstController.updateTimer), userInfo: nil, repeats: true)
            liveTimerRun = true
        }
}

Как реализовать запуск таймера в startTimerInSecondController? Спасибо


#2

Поместите таймер в синглтон.


#3

И тут все очень просто!
На помощь приходят делегаты!
создаем протокол:

protocol TimerDelegate

создаем enum (как же я без них :slight_smile: )

enum TimerState{
case start
case stop
case pause
}

в протоколе объявляем функцию:

 func updateTimerState(newState: TimerState){}

подписываемся на этот протокол в том контролере где у нас таймер

class FirstViewController: UIViewController, TimerDelegate{}

в этом же контроллере можно создать переменную для работы с таймером:

 var timerState: TimerState = .stop

в ней мы и можем контролировать процесс запуска таймера. так будет проще!
Тут нам на помощь приходят волшебные функции didSet и willSet

  var timerState: TimerState = .stop{
     didSet{
             switch timerState{
             case: .start: // запускаем таймер
             case: .stop: // останавливаем таймер и забрасываем значение
             case: .pause: // останавливаем таймер и оставляем значение
             }
      }
   }

и не забываем реализовать функцию протокола:

 func updateTimerState(newState: TimerState){
      guard  timerState != newState else { return }
      timerState = newState
 }

Теперь перейдем ко второму контроллеру:

class OtheViewController: UIViewController{}

тут мы должны создать переменную (я хочу чтоб сами поняли зачем):

var timerDelegate: TimerDelegate?

И самое главное!
При переходе с FirstViewController на OtheViewController в методе

 func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!){
 // вот тут когда дойдете до destinationViewController нужно написать:

    destinationViewController. timerDelegate = self
 }

Все! Теперь в любом екшене в OtheViewController просто пишите строчку:

timerDelegate.updateTimerState(newState: // тут вставляете что вы хотите сделать!)

Все! и Всех прошу подтянуть теорию перед тем как садиться писать код!


Переход на следующую ячейку через AlertController
#4

Нашел еще проще решение. Легче всего будет использовать NotificationCenter
Во втором контроллере код по нажатию на кнопку:

NotificationCenter.default.post(name: NSNotification.Name("TriggerMyTimer"), object: nil)

В первом контроллере во viewDidLoad:

NotificationCenter.default.addObserver(self, selector: #selector(startTimerInFirstController), name: NSNotification.Name("TriggerMyTimer"), object: nil) //подписываемся

и в классе:
deinit {
NotificationCenter.default.removeObserver(self) //отписываемся
}


#5

Решений много, главное выбрать правильное исходя из задачи!
И вот вам вопрос: как вы при вашем решении во втором контроллере узнаете запущен таймер или нет?