![](/img/trans.png)
[英]Why does my UIView.Transition not work unless wrapped in a UIView.Animate block?
[英]Why does UIView.animate work with an interactive controller transition, but UIViewPropertyAnimator doesn't?
有關設置手勢識別器的樣板,以及交互式過渡,請參閱此答案 。
我正在嘗試交互式過渡,並花了相當多的時間試圖弄清楚為什么控制器會正常轉換而不是根據手勢擦洗。 我發現它沒有用,因為我使用的是UIViewPropertyAnimator
。 切換到較舊的UIView動畫塊開箱即用。 為什么? 實施有什么區別?
func animateTransition(using transitionContext: UIViewControllerContextTransitioning)
{
// Ignore the forced unwrapping, for sake of brevity.
let view_From = transitionContext.viewController(forKey: .from)!.view!
let view_To = transitionContext.viewController(forKey: .to)!.view!
transitionContext.containerView.insertSubview(view_To, aboveSubview: view_From)
view_To.alpha = 0
// This animation block works - it will follow the progress value of the interaction controller
UIView.animate(withDuration: 1, animations: {
view_From.alpha = 0.0
view_To.alpha = 1.0
}, completion: { finished in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
// This animation block fails - it will play out normally and not be interactive
/*
let animator = UIViewPropertyAnimator(duration: 1, curve: .linear)
animator.addAnimations {
view_To.alpha = 1
view_From.alpha = 0
}
animator.addCompletion { (position) in
switch position {
case .end: print("Completion handler called at end of animation")
case .current: print("Completion handler called mid-way through animation")
case .start: print("Completion handler called at start of animation")
}
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
animator.startAnimation()
*/
}
隨着iOS 10中UIViewPropertyAnimator
引入, UIViewControllerAnimatedTransitioning
協議也得到了更新。 他們添加了一個可選的func interruptibleAnimator(using: UIViewControllerContextTransitioning)
,你不需要實現它(我想是為了向后兼容)。 但它完全針對您在此提及的用例添加:利用新的UIViewPropertyAnimator
。
所以要得到你想要的東西:首先,你必須實現interruptibleAnimator(using:)
來創建動畫師 - 你不要在animateTransition(using:)
創建它animateTransition(using:)
。
根據UIViewControllerAnimatedTransitioning
的源代碼中的注釋(重點是我的)(我不知道為什么文檔不包含這個信息):
如果可以中斷它創建的轉換,則符合對象會實現此方法。 例如,它可以返回UIViewPropertyAnimator的實例。 預計此方法將在轉換的生命周期內返回相同的實例。
您必須在轉換期間返回相同的動畫師。 這就是你會發現的原因
private var animatorForCurrentSession: UIViewImplicitlyAnimating?
我的BackAnimator
實現中的屬性 - 如果轉換尚未結束,我將當前的動畫存儲在那里以返回它。
當實現了interruptibleAnimator(using:)
,環境將使用該動畫師並使用它而不是使用animateTransition(using:)
進行動畫animateTransition(using:)
。 但是為了保持協議的契約, animateTransition(using:)
應該能夠為轉換設置動畫 - 但是你可以簡單地使用interruptibleAnimator(using:)
創建一個動畫師並在那里運行動畫。
以下是一個有效的BackAnimator
實現,您可以使用此SO問題中提到的示例。 我使用你的代碼作為基礎,但你可以簡單地交換我的BackAnimator
來實現它們,你很高興(我在他們的例子中測試它)。
class BackAnimator : NSObject, UIViewControllerAnimatedTransitioning {
// property for keeping the animator for current ongoing transition
private var animatorForCurrentTransition: UIViewImplicitlyAnimating?
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
// as per documentation, the same object should be returned for the ongoing transition
if let animatorForCurrentSession = animatorForCurrentTransition {
return animatorForCurrentSession
}
// normal creation of the propertyAnimator
let view_From = transitionContext.viewController(forKey: .from)!.view!
let view_To = transitionContext.viewController(forKey: .to)!.view!
transitionContext.containerView.insertSubview(view_To, aboveSubview: view_From)
view_To.alpha = 0
let animator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext), curve: .linear)
animator.addAnimations {
view_To.alpha = 1
view_From.alpha = 0
}
animator.addCompletion { (position) in
switch position {
case .end: print("Completion handler called at end of animation")
case .current: print("Completion handler called mid-way through animation")
case .start: print("Completion handler called at start of animation")
}
// transition completed, reset the current animator:
self.animatorForCurrentTransition = nil
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
// keep the reference to current animator
self.animatorForCurrentTransition = animator
return animator
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// animateTransition should work too, so let's just use the interruptibleAnimator implementation to achieve it
let anim = self.interruptibleAnimator(using: transitionContext)
anim.startAnimation()
}
}
另請注意, 由interruptibleAnimator(using:)
返回的動畫師不是由我們啟動的 - 環境將在適當的時候啟動它。
PS:我對這個主題的大部分知識來自於試圖實現一個開源容器,它允許在其容器之間進行自定義交互式轉換 - InteractiveTransitioningContainer 。 也許你會發現一些靈感:)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.