meteors meteors - 4 months ago 8
iOS Question

Animation callback altering a different variable

I have a button which shows a view and which automatically goes away after specified time interval. Now if the button is pressed again while the view is already visible then it should go away and show a new view and the timer for new view be reset.

On the button press I have following code:

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


To remove toast i have the following code:

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


Now even though I reset the timer each time by doing
timer.invalidate()
I get two calls in
removeToast()
which removes the newly inserted view. Can it be that
UIView.animate
be causing problems, I'm not getting how to debug the two callbacks for
removeToast()
. A demo project showing the behavior is here

NOTE: I did find some post saying to use
dispatch_after()
instead of timer, also as asked by @jervine10, but it does not suffice my scenario. As if I use
dispatch_after
then it's difficult to invalidate GCD call. Is there something that could be accomplished with NSTimers. I think that NSTimers are meant for this and there's something that I'm doing wrong.

Answer

Use dispatch_after to call a method after a set period of time instead of a timer. It would look like this:

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