我有一个Authenticator
类,其中sendEmailForPasswordRecovery
方法实施asynchronous
调用,将电子邮件发送给firebase
用户以进行密码恢复。
func sendEmailForPasswordRecovery(email: String, completion: CallBackWithError) {
FIRAuth.auth()?.sendPasswordResetWithEmail(email, completion: { (error: NSError?) in
completion(error)
})
}
我从function
UIViewController
Authenticator().sendEmailForPasswordRecovery(email, completion: { (error: NSError?) in
print("operation completed. error: \(error)")
self.completion?()
})
完成块只是调用此函数。只需隐藏 popUp 视图,淡出 模糊效果,删除其父视图。
func removeForgotPasswordScreen() {
UIView.animateWithDuration(0.5, animations: {
self.blurEffectView.alpha = 0
self.containerForEmail.alpha = 0
}) { (_: Bool) in
self.containerForEmail.hidden = true
self.blurEffectView.removeFromSuperview()
}
}
但是当执行Authenticator().sendEmailForPasswordRecovery
时,我可以看到控制台中的错误为nil。但 popUp 视图仅在40-50秒后消失。但是当我在dispatch_async
中包装完成时,我会立即得到我的结果。
Authenticator().sendEmailForPasswordRecovery(email, completion: { (error: NSError?) in
// self.completion?() <----- this was causing delay
dispatch_async(dispatch_get_main_queue(), {
self.completion?() <------ Now it updates immidiately
})
})
Firebase sendPasswordResetWithEmail
有签名:
public func sendPasswordResetWithEmail(email: String, completion: FIRSendPasswordResetCallback?)
它说
@param completion可选;一个块,当它被调用 请求完成。调用 将来在主线程上异步。
我不明白的是,为什么PopUp在首先出现一定的延迟后就消失了?dispatch_assync
是如何做到这一点的。{/ p>
答案 0 :(得分:1)
要为视图设置动画,您应该使用主队列,并且 sendPasswordResetWithEmail 不会在主队列中调用您的块,并强调:将来在主线程上异步调用。
dispatch_async 会将执行块排入线程队列,但不保证此线程是主线程或优先级较低,例如 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) ){} ,所以我猜 sendPasswordResetWithEmail 实现其方法使用DISPATCH_QUEUE_PRIORITY_DEFAULT或其他低优先级,这使得您的代码无法保证立即执行,实际上是执行&#39 ;时间没有准确确认。下面是一个测试,标签的动画有时会在很多秒后执行,有时候不会执行。
注意:处理或设置UI必须在主线程中,这只是用于测试。
class ViewController: UIViewController {
let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor()
self.label.frame = CGRectMake(0, 0, 100, 21)
self.label.text = "Label"
self.label.center = self.view.center
self.view.addSubview(self.label)
let button = UIButton(type: .System)
button.setTitle("Button", forState: .Normal)
button.frame = CGRectMake(CGRectGetMinX(self.label.frame), CGRectGetMaxY(self.label.frame) + 20, 50, 20)
button.addTarget(self, action: NSSelectorFromString("handleButtonPressed:"), forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
func handleButtonPressed(sender: UIButton) {
// the block will execute not in main queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
NSThread.sleepForTimeInterval(1)
// because the queue is not main queue, the animation not execute immediatelly, even not execute.
UIView.animateWithDuration(3, animations: {
var origin = self.label.frame.origin
origin.y += 100
self.label.frame.origin = origin
}, completion: { (complete) in
self.label.hidden = true
})
}
}
}
但是如果你换成主队列,动画将作为你的希望时间执行。
func handleButtonPressed(sender: UIButton) {
// in main queue
dispatch_async(dispatch_get_main_queue()) {
NSThread.sleepForTimeInterval(1)
UIView.animateWithDuration(3, animations: {
var origin = self.label.frame.origin
origin.y += 100
self.label.frame.origin = origin
}, completion: { (complete) in
self.label.hidden = true
})
}
}
因此,您应该执行在主队列(主线程)中扩展到UI的所有代码。
希望答案可以帮到你。这是GCD's guide,它会仔细而完整地解释。