🤖
UIView.animateWithDurationのcompletionが呼ばれないことがある?
- dismissのトランジション
- スワイプに連動したトランジション
- iOS 8.3
- もの凄い勢いでスワイプする(笑)
で起きたのでメモ。
8.3以前は動いていたコード
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let from: UIViewController! = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
let to: UIViewController! = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
…
UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
() -> Void in
// ここで色々やる
}) {
(finished) -> Void in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
}
}
という感じで、アニメーションを定義しておいて
- UIScreenEdgePanGestureRecognizer
- UIPanGestureRecognizer
と
- UIPercentDrivenInteractiveTransition
を組み合わせて、スワイプするとdismissするように作る
switch gestureRecognizer.state {
case .Began:
if location.x < 0 { return }
interactiveTransition = UIPercentDrivenInteractiveTransition()
interactionInProgress = true
dismissViewControllerAnimated(true, completion: nil)
case .Changed:
interactiveTransition?.updateInteractiveTransition(progress)
case .Ended, .Cancelled:
interactionInProgress = false
if progress > 0.35 {
interactiveTransition?.finishInteractiveTransition()
} else {
interactiveTransition?.cancelInteractiveTransition()
}
default:
return
}
こんな感じでやっていた。
起きた問題
この状態でiOS 8.3でもの凄い勢いでスワイプすると
finishInteractiveTransition
cancelInteractiveTransition
は呼ばれるけど、animateTransition
内のanimateWithDuration
のcompletion
が呼ばれないという現象が起きた。
そのせいで、トランジションが完了したと認識できずに画面がフリーズしたような状態になってしまった。
解決策
場当たり的だけど、finishInteractiveTransition
とcancelInteractiveTransition
を呼び出すルートに入るのはわかっていたので、ちょっとだけディレイさせてトランジションを強制的に終わらせるようにしたところ改善した。
もっと良い方法がないのだろうか…。
コード
- アニメーションする所
private var currentTransitionContext: UIViewControllerContextTransitioning!
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
currentTransitionContext = transitionContext
let from: UIViewController! = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
let to: UIViewController! = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
…
UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
() -> Void in
// ここで色々やる
}) {
(finished) -> Void in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
self.currentTransitionContext = nil
}
}
func forceFinish() {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.3 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
if let transitionContext = self.currentTransitionContext {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
self.currentTransitionContext = nil
}
}
}
- ジェスチャーの認識
switch gestureRecognizer.state {
case .Began:
if location.x < 0 { return }
interactiveTransition = UIPercentDrivenInteractiveTransition()
interactionInProgress = true
dismissViewControllerAnimated(true, completion: nil)
case .Changed:
interactiveTransition?.updateInteractiveTransition(progress)
case .Ended, .Cancelled:
interactionInProgress = false
if progress > 0.35 {
interactiveTransition?.finishInteractiveTransition()
} else {
interactiveTransition?.cancelInteractiveTransition()
}
forceFinish()
default:
return
}
Discussion