1

제스처 인식기 설정 및 대화식 전환과 같은 상용구에 대해서는 this answer을 참조하십시오.UIView.animate가 대화 형 컨트롤러 전환과 함께 작동하지만 UIViewPropertyAnimator가 작동하지 않는 이유는 무엇입니까?

대화 형 전환을 실험하고 있으며 제스처에 따라 컨트롤러가 정상적으로 전환되는 대신 왜 컨트롤러가 전환되는지 알아 내려고 꽤 많은 시간을 보냈습니다. 나는 UIViewPropertyAnimator을 사용하고 있기 때문에 그것이 작동하지 않는다는 것을 발견했습니다. 이전 UIView 애니메이션 블록으로 전환하면 바로 사용할 수 있습니다. 왜? 구현의 차이점은 무엇입니까? 아이폰 OS UIViewControllerAnimatedTransitioning 프로토콜이 업데이트되었다 (10)의 UIViewPropertyAnimator의 도입

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() 
    */ 
} 

답변

1

, 너무. 그들은 구현할 필요가없는 func interruptibleAnimator(using: UIViewControllerContextTransitioning) 옵션을 추가했습니다 (이전 버전과의 호환성을 위해). 하지만 여기에 언급 된 유스 케이스에 정확히 추가되었습니다 : 새로운 UIViewPropertyAnimator을 활용하십시오.

원하는 것을 얻으려면 : 먼저 interruptibleAnimator(using:)을 구현하여 애니메이터를 생성해야합니다 (animateTransition(using:)에서 생성하지 않음). UIViewControllerAnimatedTransitioning의 소스 코드에서 주석 (강조는 나의 것) (I이 문서는이 정보를 포함하지 않는 이유를 모르겠어요) 당으로

는 : 전환이 (가) 작성하는 경우

는 부합하는 객체는이 메소드를 구현 중단 될 수 있습니다. 예를 들어 UIViewPropertyAnimator의 인스턴스를 반환 할 수 있습니다. 이 메서드는 전환 수명 동안 동일한 인스턴스를 반환 할 것으로 예상됩니다.

전환 기간 동안 동일한 애니메이터를 반환해야합니다. 당신이 내 BackAnimator 구현

private var animatorForCurrentSession: UIViewImplicitlyAnimating? 

속성을 찾을 이유 - 내가 거기에 현재 애니메이터를 저장 전환이 종료되지 않은 경우 그것을 반환 할 수 있습니다.

interruptibleAnimator(using:)이 구현되면 환경에서 해당 애니메이터를 가져와 animateTransition(using:)을 사용하여 애니메이션 대신 사용합니다. 그러나 프로토콜 계약을 유지하려면 animateTransition(using:)에서 전환을 애니메이션으로 처리 할 수 ​​있어야합니다. 다만 interruptibleAnimator(using:)을 사용하여 애니메이터를 만들고 거기에서 애니메이션을 실행할 수 있습니다.

다음은 this SO question에서 언급 한 예제와 함께 사용할 수있는 작동중인 BackAnimator 구현입니다. 나는 당신의 코드를 기초로 사용했다. 그러나 당신은 단지 나의 구현을 위해 나의 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:)에 의해 반환 된 애니메이터 우리으로 시작되지 않습니다 통지 - 때 적절한 환경은 시작됩니다.

P .: 본 주제에 대한 대부분의 지식은 해당 콘테 게들 사이에 사용자 정의 대화식 전환을 허용하는 오픈 소스 컨테이너 구현을 시도한 것입니다. - InteractiveTransitioningContainer. 어쩌면 거기에서 영감을 얻을 수있을 것입니다. :).