割とマイナーな気がしますが、少しハマったのでメモです。
条件と再現方法
- navigationTranslationAnimatorをセットしてNavigationControllerの遷移アニメーションをカスタムしており、遷移アニメーションとして
UIView.animate
のアニメーションブロック内でframe
を操作している。(例えば一覧画面から選択されたセルにセットされている画像が、そのまま次の画面に移動するような遷移アニメーションなど) -
UIViewControllerInteractiveTransitioning
のサブクラスを使ってインタラクティブジェスチャを実装している。
上記の条件のときに、ジェスチャで途中までアニメーションを進めて途中で指を離してキャンセルすると、元の状態に戻るアニメーションがiOS10の場合でのみおかしくなります。(サイズが縮んでいくようなアニメーションになる)
解決法
iOS10の場合だけ UIViewPropertyAnimator
を使うことで解決しました。
iOS10未満は対応していないため、バージョンで分岐させる必要があります。
let animation: () -> Void = {
// 一例です
fromView.alpha = 0
toView.alpha = 1.0
}
let completion: () -> Void = {
// 一例です
let isCancelled = transitionContext.transitionWasCancelled
transitionContext.completeTransition(!isCancelled)
}
if UIDevice.current.systemVersion.compare("11", options: .numeric) == .orderedAscending
&& UIDevice.current.systemVersion.compare("10", options: .numeric) != .orderedAscending {
// Only for iOS10 animation bug fix.
let animator = UIViewPropertyAnimator(duration: duration, curve: .easeIn, animations: {
animation()
})
animator.addCompletion({ (position) in
completion()
})
animator.startAnimation()
} else {
UIView.animate(withDuration: duration, delay: 0, options: [.beginFromCurrentState, .curveEaseIn], animations: {
animation()
}) { (finished) in
completion()
}
}
別の方法として、 frame
を使う代わりに center
と transform.scale
を組み合わせるという方法もありますが、 UIImageView
の場合だと contentMode
に関係なく引き伸ばされて画質が粗くなったり、比率が崩れたりといった問題があります。
10/30/2017 追記
iOS11ではPropertyAnimatorを使うとInteractiveTransitionがうまく実行されず、UIView.animateであればiOS10のときのような不具合が発生することなく動きました。
このことから、iOS10のときにのみPropertyAnimatorを使う方法に変えないといけないようです。