Обратный вызов завершения отклонения выполняется после отмены перехода настраиваемого контроллера представления

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

  • UIViewControllerAnimatedTransitioning
  • UIPercentDrivenInteractiveTransition
  • UIViewControllerTransitioningDelegate
  • UIViewPropertyAnimator

Я хочу добиться того, чтобы я мог представить контроллер представления модально, а затем использовать UIPanGestureRecognizer, чтобы отклонить представленный контроллер представления, перетащив его вниз. Если я уберу палец в верхней половине экрана, переход должен быть отменен, в противном случае переход будет успешно завершен.

Вот код проблемы:

    func handlePanGesture(gestureRecognizer: UIPanGestureRecognizer) {
        let translation = gestureRecognizer.translation(in: presentedViewController.view)
        switch gestureRecognizer.state {
        case .began:
            interacting = true
            presentingViewController.dismiss(animated: true) {
                print("Dismissal Completion Callback is Called.")
                // How can I know the dismissal is successful or cancelled.
            }
        case .changed:
            let fraction = (translation.y / UIScreen.main.bounds.height)
            update(fraction)
        case .ended, .cancelled:
            interacting = false
            if (percentComplete > 0.5) {
                finish()
            } else {
                cancel()
            }
        default:
            break
        }
    }

Мой код отлично работает с аспектами пользовательского интерфейса и взаимодействия, но я не понимаю поведения функции func dismiss(animated flag: Bool, completion: (() -> Void)? = nil).

В .began случае Pan Gesture вызывается presentingViewController.dismiss(animated: true) { ... }, поэтому начинается настраиваемый переход. Но обратный вызов completion всегда вызывается независимо от того, отменен переход увольнения или нет.

Я смотрел эти видео WWDC:

введите описание изображения здесь

Они используют пример кода для демонстрации настраиваемого перехода с UINavigationController и не упоминают обратный вызов увольнения.

presentingViewController.dismiss(animated: true) {
        debugPrint("Dismissal Completion Called")
        debugPrint("[ presentedViewController.transitionCoordinator?.isCancelled \(self.presentedViewController.transitionCoordinator?.isCancelled) ]")
}

В документе о параметре completion:

завершение

Блок, выполняемый после закрытия контроллера представления. Этот блок не имеет возвращаемого значения и не принимает параметров. Вы можете указать для этого параметра nil.

Вопрос

Каково истинное значение Completion, поскольку он всегда вызывается после отмены или завершения пользовательского перехода?

Когда я использую настраиваемый переход с презентацией и увольнением, как лучше всего обрабатывать реальное завершение увольнения для обновления пользовательского интерфейса и данных?


ios
person yunhao    schedule 10.07.2019    source источник


Ответы (2)


После небольшого исследования и тестирования - да, я бы сказал, что это немного сбивает с толку.

Блок завершения НЕ вызывается после закрытия VC. Скорее, он вызывается после возврата функции.

Итак, предполагая, что вы реализуете UIPercentDrivenInteractiveTransition, .dismiss() запускает ваш код перехода и возвращается, когда вы cancel() или finish(), но его блок завершения не знает, что вы на самом деле сделали с переходом.

Я уверен, что есть разные подходы к этому ... но моей первой мыслью было бы поместить ваш «код завершения» в case .ended, .cancelled:, где вы (уже) определяете, удалять ли VC (вызывать ли .cancel() или .finish() ).

person DonMag    schedule 10.07.2019

Наконец, я нахожу кое-что полезным в документе Apple:

В конце анимации перехода очень важно вызвать метод completeTransition:. Вызов этого метода сообщает UIKit, что переход завершен и пользователь может начать использовать представленный контроллер представления. Вызов этого метода также запускает каскад других обработчиков завершения, в том числе один из метода presentViewController: animated: completion: и собственный метод animationEnded: объекта-аниматора. Лучше всего вызывать метод completeTransition: в обработчике завершения вашего блока анимации.

Поскольку переходы можно отменить, следует использовать возвращаемое значение метода transitionWasCancelled объекта контекста, чтобы определить, какая очистка требуется. Когда презентация отменена, ваш аниматор должен отменить все изменения, внесенные в иерархию представления. Для успешного увольнения требуются аналогичные действия.

Таким образом, обратный вызов завершения present(_:animated:completion:) и dismiss(animated:completion:) не имеет никаких параметров, чтобы указать, завершен ли переход или отменен. Оба они вызываются, если метод transitionContext.completeTransition(_:) вызывается при завершении или отмене перехода. Такое поведение спроектировано намеренно.

person yunhao    schedule 12.07.2019