ios — интерактивный блок завершения перехода для UIView.animateWithDuration никогда не вызывается внутри animateTransition

Я использую настраиваемый интерактивный всплывающий переход с распознавателем жестов панорамирования. У меня есть подкласс от UIPercentDrivenInteractiveTransition для обработки действий пользователя:

class InteractiveTransitionManager: UIPercentDrivenInteractiveTransition {

  var viewController: UIViewController?
  var interactive: Bool = false

  var popInProgress = false
  var startX: CGFloat = 0

  init(viewController: UIViewController) {
    super.init()
    self.viewController = viewController
    self.viewController?.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan:"))
  }

  func handlePan(pan: UIPanGestureRecognizer) {
    if pan.velocityInView(pan.view!.superview!).x > 0 && !popInProgress {
      popInProgress = true
      interactive = true
      startX = pan.locationInView(pan.view!.superview!).x
      viewController?.navigationController?.popViewControllerAnimated(true)
      return
    }
    let curX = pan.locationInView(pan.view!.superview!).x
    let progress: CGFloat = max(0, curX - startX) / pan.view!.frame.width
    if pan.state == .Changed {
      updateInteractiveTransition(progress)
    } else if pan.state == .Ended || pan.state == .Cancelled {
      if progress > 0.5 {
        finishInteractiveTransition()
      } else {
        cancelInteractiveTransition()
      }
      popInProgress = false
      interactive = false
    }
  }

}

И у меня есть класс InteractivePopTransition для анимации перехода:

class InteractivePopTransition: NSObject, UIViewControllerAnimatedTransitioning {

func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
    return 0.3
}

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
    let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
    let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
    let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
    let containterView = transitionContext.containerView()

    let targetFrame = transitionContext.finalFrameForViewController(toVC)

    toView.frame = CGRect(x: -targetFrame.size.width, y: targetFrame.origin.y, width: targetFrame.width, height: targetFrame.height)
    containterView?.insertSubview(toView, belowSubview: fromView)

    UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
        toView.frame = targetFrame
        fromView.frame.origin.x = CGRectGetMaxX(targetFrame)
        }, completion: { b in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
    })
}

}

У меня проблема в том, что блок завершения в UIView.animateWithDuration иногда не вызывается. Почему это происходит и как это исправить?


person AlexanderBozhko    schedule 17.03.2016    source источник


Ответы (1)


Когда вызывается animateTransition(transitionContext:), а взаимодействие с пользователем не было интерактивным (например, триггер программной анимации), кажется, что в системе возникла проблема с синхронизацией, и completion UIView.animateWithDuration не вызывается, как вы описали.

Я столкнулся с этим в своей работе и смог решить проблему, явно вызвав finishInteractiveTransition для объекта UIPercentDrivenInteractiveTransition из animateTransition(transitionContext:) перед вызовом UIView.animateWithDuration(...).

Это было простое решение для моего случая, потому что мой объект UIViewControllerAnimatedTransitioning имел экземпляр подкласса UIPercentDrivenInteractiveTransition, и я предоставил свойство interactionInProgress, чтобы определить, когда я должен явно вызывать finishInteractiveTransition.

Если вы явно вызовете finishInteractiveTransition для объекта интерактивного перехода, будет вызван блок завершения.

class InteractivePopTransition: NSObject, UIViewControllerAnimatedTransitioning {
  private var interactiveTransitionManager: InteractiveTransitionManager
  ...
  func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    ...
    if !interactiveTransitionManager.isInProgress {
      // Weird state where system is telling you to animate a transition, but there
      // is no interactive transition occurring. Manually finish the non-existant
      // interactive transition so system will call other animation callbacks
      // appropriately to complete the entire transition
      interactiveTransitionManager.finish()
    }
    UIView.animateWithDuration(...)
  }  
}

Другое решение — поместить UIView.animateWithDuration внутри асинхронного блока отправки с небольшой задержкой.

person Steven McCracken    schedule 12.11.2019