动画回调改变另一个变量

时间:2015-08-31 15:45:35

标签: ios swift animation closures

我有一个按钮,显示一个视图,并在指定的时间间隔后自动消失。现在,如果在视图已经可见的情况下再次按下按钮,则它应该消失并显示新视图并重置新视图的计时器。

按下按钮,我有以下代码:

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就是为了这个而且我做错了。

2 个答案:

答案 0 :(得分:1)

很抱歉没有看到您的示例项目,感谢您指导我。我可以清楚地看到问题所在,解决方案非常简单。更改删除吐司:

popAlertMessageController(self, title: "Alert", message: "Your message")

基本上,问题是您在动画块中捕获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。因此,一旦动画块异步执行,它们将删除上一个函数中的 new toastView集。

解决方案很简单,并且还修复了可能的竞争条件,即捕获变量中的toastView。最后,我们检查实例变量是否等于我们要移除的视图,我们将其置为空白。

提示:考虑使用toastView的{​​{1}}参考

答案 1 :(得分: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()
}