改变不同变量的动画回调
Animation callback altering a different variable
我有一个显示视图并在指定时间间隔后自动消失的按钮。现在,如果在视图已经可见时再次按下按钮,那么它应该消失并显示一个新视图,并且重置新视图的计时器。
在按下按钮时,我有以下代码:
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
})
}
要删除 toast,我有以下代码:
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
注意: 我确实发现一些 post 说使用 dispatch_after()
而不是计时器,@jervine10 也提出了这样的要求,但这还不够我的场景。就好像我使用 dispatch_after
那么很难使 GCD 调用无效。有什么可以用 NSTimers 完成的吗?我认为 NSTimers 就是为此而设计的,但我做错了什么。
使用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()
}
很抱歉没有看到您的示例项目,感谢您指导我。我可以清楚地看到问题出在哪里,解决方案也非常简单。将删除吐司更改为:
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
。因此,一旦动画块异步执行,它们将删除之前函数中设置的 new toastView
解决方法很简单,也修复了可能的竞争条件,那就是将toastView捕获到一个变量中。最后,我们检查实例变量是否等于我们要删除的视图,我们将其无效。
提示:考虑使用 weak
参考 toastView
我有一个显示视图并在指定时间间隔后自动消失的按钮。现在,如果在视图已经可见时再次按下按钮,那么它应该消失并显示一个新视图,并且重置新视图的计时器。
在按下按钮时,我有以下代码:
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
})
}
要删除 toast,我有以下代码:
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
注意: 我确实发现一些 post 说使用 dispatch_after()
而不是计时器,@jervine10 也提出了这样的要求,但这还不够我的场景。就好像我使用 dispatch_after
那么很难使 GCD 调用无效。有什么可以用 NSTimers 完成的吗?我认为 NSTimers 就是为此而设计的,但我做错了什么。
使用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()
}
很抱歉没有看到您的示例项目,感谢您指导我。我可以清楚地看到问题出在哪里,解决方案也非常简单。将删除吐司更改为:
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
。因此,一旦动画块异步执行,它们将删除之前函数中设置的 new toastView
解决方法很简单,也修复了可能的竞争条件,那就是将toastView捕获到一个变量中。最后,我们检查实例变量是否等于我们要删除的视图,我们将其无效。
提示:考虑使用 weak
参考 toastView