Перетаскивание интерактивного перехода вне синхронизации c с помощью жеста панорамирования - PullRequest
0 голосов
/ 15 января 2020

Я пытаюсь реализовать пользовательский интерактивный переход с перетаскиванием, используя UIViewControllerAnimatedTransitioning и UIPercentDrivenInteractiveTransition.

Вот код аниматора для отклонения, он просто полностью перемещает представление вниз из кадр:

final class SheetDismissAnimator: NSObject, UIViewControllerAnimatedTransitioning {

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        0.5
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromVC = transitionContext.viewController(forKey: .from) else { return }

        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
            fromVC.view.frame.origin.y = transitionContext.containerView.frame.height
        }, completion: { _ in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        })
    }

}

А вот код контроллера модального представления, который я пытаюсь отклонить, к которому прикреплен распознаватель жестов панорамирования:

final class ModalViewController: UIViewController {

    private let customTransitionDelegate = SheetTransitioningDelegate()
    private var interactionController: UIPercentDrivenInteractiveTransition?

    init() {
        super.init(nibName: nil, bundle: nil)

        modalPresentationStyle = .custom
        transitioningDelegate = customTransitionDelegate
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .red
        view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:))))
    }

    @objc func handleGesture(_ gesture: UIPanGestureRecognizer) {
        let translate = gesture.translation(in: view)
        let percent   = translate.y / view.bounds.height

        switch gesture.state {
        case .began:
            interactionController = UIPercentDrivenInteractiveTransition()
            customTransitionDelegate.interactionController = interactionController
            dismiss(animated: true)
        case .changed:
            interactionController?.update(percent)
        case .cancelled:
            interactionController?.cancel()
        case .ended:
            let velocity = gesture.velocity(in: gesture.view)
            if percent > 0.5 || velocity.y > 700 {
                interactionController?.finish()
            } else {
                interactionController?.cancel()
            }
            interactionController = nil
        default:
            break
        }

    }
}

Соответствующая часть Я считаю, что это функция handleGesture(_ gesture: UIPanGestureRecognizer), которая вычисляет процент для продвижения анимации. Вставил весь код для ясности.

Я ожидаю, что анимация будет следовать за жестом панорамирования линейно, но вместо этого происходит следующее:

https://media.giphy.com/media/Wn6kAgngfHeynjhAbm/giphy.gif

1 Ответ

0 голосов
/ 16 января 2020

Я решил проблему, переключившись на UIViewPropertyAnimator:

final class SheetDismissAnimator: NSObject, UIViewControllerAnimatedTransitioning {

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        0.5
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext),
                                                  timingParameters: UISpringTimingParameters(dampingRatio: 1))
        propertyAnimator.addAnimations {
            switch self.type {
            case .presented:
                guard let toVC = transitionContext.viewController(forKey: .to) else { break }
                toVC.view.frame = transitionContext.finalFrame(for: toVC)
            case .dismissed:
                transitionContext.view(forKey: .from)?.frame.origin.y = transitionContext.containerView.frame.maxY
            }
        }
        propertyAnimator.addCompletion { _ in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }

        propertyAnimator.startAnimation()
    }

}

Я также добавил следующее в свой код обработки жестов, который является основной c попыткой передать скорость жеста в анимацию. скорость.

case .ended:
    if percent > 0.45 || velocity.y > 700 {
        interactor.timingCurve = UICubicTimingParameters(animationCurve: .linear)
        interactor.completionSpeed = max(1, (view.frame.height * (1 / interactor.duration) / velocity.y))
        interactor.finish()
    } else {
        interactor.cancel()
    }
    isInteractive = false
...