UIPercentDrivenInteractiveTransition produzindo animação estranha quando concluído
-
21-12-2019 - |
Pergunta
Estou usando uma transição push personalizada interativa com um UIPercentDrivenInteractiveTransition
.O reconhecedor de gestos chama com sucesso o controlador de interação updateInteractiveTransition
.Da mesma forma, a animação é concluída com sucesso quando eu chamo o controlador de interação finishInteractiveTransition
.
Mas, às vezes, recebo um pouco mais de animação perturbadora no final (onde parece repetir a última parte da animação).Com animações razoavelmente simples, raramente vejo esse sintoma no iPhone 5 (embora eu o veja rotineiramente no simulador ao trabalhar em um laptop lento).Se eu tornar a animação mais cara computacionalmente (por exemplomuitas sombras, múltiplas visualizações animando diferentes direções, etc.), a frequência desse problema no dispositivo aumenta.
Alguém mais viu esse problema e descobriu uma solução diferente de simplificar as animações (o que eu deveria fazer de qualquer maneira) e/ou escrever meus próprios controladores de interação?O UIPercentDrivenInteractiveTransition
abordagem tem uma certa elegância, mas estou desconfortável com o fato de que ela se comporta mal de forma não determinística.Outras pessoas viram esse comportamento?Alguém conhece outras soluções?
Para ilustrar o efeito, veja a imagem abaixo.Observe como a segunda cena, a vista vermelha, quando a animação termina, parece repetir a última parte da animação uma segunda vez.
Esta animação é gerada por:
ligando repetidamente
updateInteractiveTransition
, progredindo atualização de 0% a 40%;pausando momentaneamente (para que você possa diferenciar entre a transição interativa e a animação de conclusão resultante de
finishInteractiveTransition
);então ligando
finishInteractiveTransition
para completar a animação;ea animação do controlador de animação
completion
bloquear chamadascompleteTransition
para otransitionContext
, para limpar tudo.
Fazendo alguns diagnósticos, parece que é esta última etapa que aciona aquela animação estranha.O bloco de conclusão do controlador de animação é chamado quando a animação termina, mas assim que eu chamo completeTransition
, às vezes ele repete a última parte da animação (principalmente ao usar animações complexas).
Não creio que seja relevante, mas este é o meu código para configurar o controlador de navegação para realizar transições interativas:
- (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;
}
Meu 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
Observe, quando coloco a instrução de log onde chamo completeTransition
, posso ver que essa animação estranha acontece depois que eu ligo completeTransition
(mesmo que a animação tenha sido realmente feita naquele ponto).Isto sugeriria que essa animação extra pode ter sido resultado da chamada para completeTransition
.
Para sua informação, fiz este experimento com um reconhecedor de gestos:
- (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];
}
}
}
Eu também fiz isso ligando para o updateInteractiveTransition
e finishInteractiveTransition
manualmente (eliminando o reconhecedor de gestos da equação), e ainda exibe este comportamento estranho:
[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];
});
});
Resumindo, concluí que este é um problema isolado para UIPercentDrivenInteractiveTransition
com animações complexas.Posso minimizar o problema simplificando-os (por ex.instantâneos e visualizações instantâneas animadas).Eu também suspeito que poderia resolver isso não usando UIPercentDrivenInteractiveTransition
e escrever meu próprio controlador de interação, que faria a animação sozinho, sem tentar interpolar o animationWithDuration
bloquear.
Mas eu queria saber se alguém descobriu algum outro truque para usar UIPercentDrivenInteractiveTransition
com animações complexas.
Solução
Eu vi algo semelhante.Eu tenho duas soluções possíveis.Uma é usar o desempenho atrasado no manipulador de conclusão da animação:
} 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;
}];
A outra possibilidade é:não use animação percentual!Eu tenho nunca tive um problema como este ao conduzir a animação personalizada interativa eu mesmo manualmente.
Outras dicas
Este problema surge apenas no simulador.
SOLUÇÃO:self.interactiveAnimator.completionSpeed = 0,999;
bug relatado aqui: http://openradar.appspot.com/14675246
O motivo desse erro no meu caso foi definir o quadro da visualização animada várias vezes.Estou configurando o quadro de visualização apenas UMA VEZ e isso resolveu meus problemas.
Então neste caso, o frame de "toViewController.view" foi definido DUAS VEZES, fazendo com que a animação tivesse um comportamento indesejado