2015-08-31 2 views
3

У меня есть кнопка, которая показывает представление и которая автоматически уходит после указанного интервала времени. Теперь, если кнопка снова нажата, когда представление уже видно, оно должно исчезнуть и отобразить новое представление, а таймер для нового представления будет сброшен.Обратный вызов анимации, изменяющий другую переменную

На нажатия кнопки я следующий код:

func showToast() { 
    timer?.invalidate() 
    timer = nil 

    removeToast() 

    var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate 
    var toAddView = appDelegate.window! 

    toastView = UIView(frame: CGRectMake(0, toAddView.frame.height, toAddView.frame.width, 48)) 
    toastView.backgroundColor = UIColor.darkGrayColor() 
    toAddView.addSubview(toastView) 

    timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: Selector("removeToast"), userInfo: nil, repeats: false) 

    UIView.animateWithDuration(0.5, animations: {() -> Void in 
     self.toastView.frame.origin.y -= 48 
    }) 
} 

Чтобы удалить тост я иметь следующий код:

func removeToast() { 
    if toastView != nil { 
     UIView.animateWithDuration(0.5, 
      animations: {() -> Void in 
       self.toastView.frame.origin.y += 48 
      }, 
      completion: {(completed: Bool) -> Void in 
       self.toastView.removeFromSuperview() 
       self.toastView = nil 
      }) 
    } 
} 

Теперь даже если я сбросить таймер каждый раз, делая timer.invalidate() я получаю два вызова в removeToast(), который удаляет вновь вставленный вид. Может быть, что UIView.animate вызывает проблемы, я не получаю отладки двух обратных вызовов для removeToast(). Демонстрационный проект, показывающий поведение является here

Примечание: Я нашел некоторую почту о том, чтобы использовать dispatch_after() вместо таймера, также, как спросил @ jervine10, но не хватает моего сценария. Как будто я использую dispatch_after, тогда сложно аннулировать вызов GCD. Есть что-то, что можно было бы сделать с помощью NSTimers. Я думаю, что NSTimers предназначены для этого, и есть что-то, что я делаю неправильно.

+0

Просто за то, что это стоит, я получаю то же самое, в результате чего таймер не будет недействительным, и даже установить его в ноль, он снова называет себя. – AndyDunn

+0

@AndyDunn У меня было обходное решение проблемы, удалив блок завершения удаления toastView. Теперь я сохраняю файл toastView и скрываю его вместо удаления из супервизора, но все равно получаю два вызова таймера. – meteors

+0

В конце я просто добавил флаг, чтобы проверить, следует ли остановить таймер. Далеко не идеален, но работает надежно. – AndyDunn

ответ

1

Извините, что не видел ваш образец проекта, и спасибо, что направил меня к нему. Я четко вижу, где проблема, и решение супер просто. Изменить удалить тост это:

func removeToast() { 

    guard let toastView = self.toastView else { 
     return 
    } 

    UIView.animateWithDuration(0.1, 
     animations: {() -> Void in 
      toastView.frame.origin.y += 48 
     }, 
     completion: {(completed: Bool) -> Void in 
      toastView.removeFromSuperview() 

      if self.toastView == toastView { 
       self.toastView = nil 
      } 
     }) 
} 

В основном, проблема заключается в том, что вы захвата self в анимации блоков, не toastView. Итак, как только блоки анимации выполняются асинхронно, они удалят новыйtoastView, установленный в предыдущей функции.

Решение прост, а также фиксирует возможные условия гонки, а именно, чтобы захватить toastView в переменной. Наконец, мы проверяем, равна ли переменная экземпляра виду, которое мы удаляем, мы аннулируем его.

Совет: Рассмотрите возможность использования weak ссылки для toastView

0

Используйте метод dispatch_after для вызова метода после заданного периода времени вместо таймера. Это будет выглядеть так:

let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(2 * NSEC_PER_SEC)) 
dispatch_after(popTime, dispatch_get_main_queue()) {() -> Void in 
    self.removeToast() 
} 
+0

и как я могу аннулировать функцию отправки от вызова? – meteors

+0

Вы бы этого не сделали. Зачем вам это недействительным? – jervine10

+0

Просто взгляните на функцию removeToast, я не хочу, чтобы она была вызвана снова. Если он будет вызван, он удалит toastView, и это то, что происходит в настоящее время. – meteors

Смежные вопросы