UIPercentDrivenInteractiveTransition придание посторонней анимации

17

Я использую интерактивный пользовательский переход с UIPercentDrivenInteractiveTransition . Средство распознавания жестов успешно вызывает updateInteractiveTransition контроллера взаимодействия. Аналогично, анимация успешно завершается, когда я вызываю finishInteractiveTransition контроллера взаимодействия.

Но иногда я получаю дополнительный бит отвлекающей анимации в конце (где, похоже, повторяется вторая часть анимации). При достаточно простых анимациях я редко вижу этот симптом на iPhone 5 (хотя я регулярно вижу его на симуляторе при работе на медленном ноутбуке). Если сделать анимацию более дорогостоящей (т. Е. Много теней, нескольких видов, анимационных разных направлениях и т. Д.), Частота этой проблемы на устройстве увеличивается.

Кто-нибудь еще видел эту проблему и выяснил решение, отличное от оптимизации анимаций (что я, по общему признанию, должен делать в любом случае) и / или написания своих собственных контроллеров взаимодействия? Подход UIPercentDrivenInteractiveTransition имеет определенную элегантность, но мне неловко с тем, что он неверно ведет себя недетерминированно. Кто-нибудь видел это поведение? Кто-нибудь знает о других решениях?

Чтобы проиллюстрировать эффект, см. изображение ниже. Обратите внимание, что вторая сцена, красная точка, когда заканчивается анимация, повторяет вторую часть своей анимации второй раз.

Эта анимация генерируется:

  • повторно вызывает updateInteractiveTransition , прогрессируя обновление от 0% до 40%;

  • мгновенно приостанавливается (так что вы можете различать интерактивный переход и анимацию завершения из finishInteractiveTransition );

  • , затем вызов finishInteractiveTransition для завершения анимации; и

  • Блок completion анимации контроллера анимации вызывает completeTransition для transitionContext , чтобы очистить все.

Выполняя некоторые диагностические операции, похоже, что именно этот последний шаг вызывает этот посторонний бит анимации. Блок завершения анимационного контроллера вызывается, когда анимация завершена, но как только я вызываю completeTransition , он иногда повторяет последний бит анимации (особенно при использовании сложных анимаций).

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

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.navigationController.delegate = self;

    self.interationController = [[UIPercentDrivenInteractiveTransition alloc] init];
}

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)operation
                                               fromViewController:(UIViewController*)fromVC
                                                 toViewController:(UIViewController*)toVC
{
    if (operation == UINavigationControllerOperationPush)
        return [[PushAnimator alloc] init];

    return nil;
}

- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController*)navigationController
                          interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController
{
    return self.interationController;
}

My PushAnimator :

@implementation PushAnimator

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return 5.0;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController* toViewController   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

    [[transitionContext containerView] addSubview:toViewController.view];
    toViewController.view.frame = CGRectOffset(fromViewController.view.frame, fromViewController.view.frame.size.width, 0);;

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        toViewController.view.frame = fromViewController.view.frame;
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

@end

Обратите внимание, что когда я добавляю оператор logging, где я вызываю completeTransition , я вижу, что этот посторонний бит анимации происходит после вызова completeTransition (хотя анимация действительно была выполнена в этой точке). Это предполагает, что эта дополнительная анимация могла быть результатом вызова completeTransition .

FYI, я сделал этот эксперимент с распознающим жестом:

- (void)handlePan:(UIScreenEdgePanGestureRecognizer *)gesture
{
    CGFloat width = gesture.view.frame.size.width;

    if (gesture.state == UIGestureRecognizerStateBegan) {
        [self performSegueWithIdentifier:@"pushToSecond" sender:self];
    } else if (gesture.state == UIGestureRecognizerStateChanged) {
        CGPoint translation = [gesture translationInView:gesture.view];
        [self.interactionController updateInteractiveTransition:ABS(translation.x / width)];
    } else if (gesture.state == UIGestureRecognizerStateEnded ||
               gesture.state == UIGestureRecognizerStateCancelled)
    {
        CGPoint translation = [gesture translationInView:gesture.view];
        CGPoint velocity    = [gesture velocityInView:gesture.view];
        CGFloat percent     = ABS(translation.x + velocity.x * 0.25 / width);

        if (percent < 0.5 || gesture.state == UIGestureRecognizerStateCancelled) {
            [self.interactionController cancelInteractiveTransition];
        } else {
            [self.interactionController finishInteractiveTransition];
        }
    }
}

Я также сделал это, вызвав updateInteractiveTransition и finishInteractiveTransition вручную (исключая распознаватель жестов из уравнения), и он по-прежнему демонстрирует это странное поведение:

[self performSegueWithIdentifier:@"pushToSecond" sender:self];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [self.interactionController updateInteractiveTransition:0.40];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.interactionController finishInteractiveTransition];
    });
});

В заключение я пришел к выводу, что это проблема, изолированная от UIPercentDrivenInteractiveTransition со сложными анимациями. Я могу свести к минимуму проблему, упростив их (например, снимки моментальных снимков и анимированные снимки). Я также подозреваю, что смогу решить эту проблему, не используя UIPercentDrivenInteractiveTransition и создав свой собственный контроллер взаимодействия, который будет делать сама анимация, не пытаясь интерполировать блок animationWithDuration .

Но мне было интересно узнать, кто-нибудь из других приемов использовал UIPercentDrivenInteractiveTransition со сложными анимациями.     

задан Rob 13.04.2014 в 21:24
источник
  • Я сообщал об этом Apple в обратном направлении во время бета-тестирования iOS 7 (например, в августе 2013 года). Они закрыли его как чисто симуляторную ошибку (и дубликат, ошибка 10535951). Можете ли вы на самом деле воспроизвести его на устройстве? –  matt 13.04.2014 в 23:51
  • Кстати, обходным решением может быть установка завершенияSpeed ​​на 0,5 в момент завершения взаимодействия пользователя. –  matt 13.04.2014 в 23:52
  • @matt Да, я могу воспроизвести его на устройстве. Сначала, как и Apple, я отклонил это как ошибку симулятора, но когда я сделал анимацию более сложной (особенно, когда я добавляю тени к «видеть» вид контроллера и анимировал представление «от» контроллера представления одновременно ), Я заметил, что я начал испытывать его с перерывами на своем iPhone 5 (и я ставлю, что я могу даже увидеть его еще больше на более медленном устройстве, таком как iPhone 4). По общему признанию, с короткой продолжительностью он почти выглядит как прерывистый эффект отскока, поэтому я сомневаюсь, что мои пользователи когда-нибудь заметят, но я это сделаю. –  Rob 14.04.2014 в 00:07
  • Кстати, я попытался сыграть с завершением (и завершением), но ничего не получится, но я попробую еще раз. –  Rob 14.04.2014 в 00:07
  • И, пожалуйста, сделайте файл с ошибкой, особенно. так как у вас есть этот хороший пример, который можно воспроизвести даже на устройстве! –  matt 14.04.2014 в 00:59
Показать остальные комментарии

3 ответа

6

Я видел нечто подобное. У меня есть два возможных решения. Один из них - использовать задержанную производительность в обработчике завершения анимации:

} completion:^(BOOL finished) {
        double delayInSeconds = 0.1;
        dispatch_time_t popTime = 
             dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            BOOL cancelled = [transitionContext transitionWasCancelled];
            [transitionContext completeTransition:!cancelled];
        });
       self.interacting = NO;
}];

Другая возможность: не используйте анимацию процента-драйва! У меня never возникла такая проблема, когда вручную управляйте интерактивной пользовательской анимацией .

    
ответ дан matt 13.04.2014 в 23:54
  • Элегантный по своей простоте. Задержка зафиксировала его как на симуляторе, так и на устройстве, хотя я, вероятно, вообще удалю UIPercentDrivenInteractiveTransition. Хорошо знать, что есть такая легкая работа. –  Rob 14.04.2014 в 00:47
11

Эта проблема возникает только в симуляторе.

РЕШЕНИЕ: self.interactiveAnimator.completionSpeed ​​= 0.999;

Об этом сообщается здесь: Ссылка

    
ответ дан andrei 23.03.2017 в 10:42
  • Проблема проявляется наиболее легко в симуляторе, но с достаточно сложными представлениями вы также можете видеть это на устройствах. –  Rob 23.03.2017 в 19:09
  • приятно знать, спасибо –  andrei 24.03.2017 в 11:15
  • Это должен быть ответ «Принятый»! Решена проблема для iOS 10.3 –  Alexey 03.04.2017 в 12:39
  • Я просто хочу добавить, что проблема возникает в симуляторе и на устройстве, в конце и в начале анимации. –  Marcin 25.04.2018 в 13:44
0

Причина этой ошибки в моем случае заключалась в том, что кадр анимации несколько раз. Я устанавливаю рамку просмотра только один раз, и он исправил мои проблемы.

Итак, в этом случае рамка «toViewController.view» была установлена ​​TWICE, что делает анимацию нежелательным поведением

    
ответ дан Lucas Chwe 14.05.2015 в 21:57
  • что не так с установкой его дважды? Я имею в виду, что вы можете, например, установить рамку toView слева от экрана, а затем оживить ее до положения экрана. Что именно мне не хватает? –  andrei 23.03.2017 в 10:32