Tive um problema em que havia uma série de sequências CATransition / CAAnimation sobrepostas, todas as quais eu precisava para realizar operações personalizadas quando as animações parassem, mas eu queria apenas um manipulador delegado para animationDidStop.
No entanto, eu tive um problema, não parecia haver uma maneira de identificar exclusivamente cada CATransition / CAAnimation no delegado animationDidStop.
Resolvi esse problema por meio do sistema de chave / valor exposto como parte do CAAnimation.
Ao iniciar sua animação, use o método setValue em CATransition / CAAnimation para definir seus identificadores e valores a serem usados quando o animationDidStop for acionado:
-(void)volumeControlFadeToOrange
{
CATransition* volumeControlAnimation = [CATransition animation];
[volumeControlAnimation setType:kCATransitionFade];
[volumeControlAnimation setSubtype:kCATransitionFromTop];
[volumeControlAnimation setDelegate:self];
[volumeControlLevel setBackgroundImage:[UIImage imageNamed:@"SpecialVolume1.png"] forState:UIControlStateNormal];
volumeControlLevel.enabled = true;
[volumeControlAnimation setDuration:0.7];
[volumeControlAnimation setValue:@"Special1" forKey:@"MyAnimationType"];
[[volumeControlLevel layer] addAnimation:volumeControlAnimation forKey:nil];
}
- (void)throbUp
{
doThrobUp = true;
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionFade];
[animation setSubtype:kCATransitionFromTop];
[animation setDelegate:self];
[hearingAidHalo setBackgroundImage:[UIImage imageNamed:@"m13_grayglow.png"] forState:UIControlStateNormal];
[animation setDuration:2.0];
[animation setValue:@"Throb" forKey:@"MyAnimationType"];
[[hearingAidHalo layer] addAnimation:animation forKey:nil];
}
Em seu delegado animationDidStop:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag{
NSString* value = [theAnimation valueForKey:@"MyAnimationType"];
if ([value isEqualToString:@"Throb"])
{
//... Your code here ...
return;
}
if ([value isEqualToString:@"Special1"])
{
//... Your code here ...
return;
}
//Add any future keyed animation operations when the animations are stopped.
}
O outro aspecto disso é que ele permite que você mantenha o estado no sistema de emparelhamento de valor-chave em vez de ter que armazená-lo em sua classe de delegado. Quanto menos código, melhor.
Certifique-se de verificar a Referência da Apple sobre codificação de pares de valores-chave .
Existem técnicas melhores para identificação de CAAnimation / CATransition no delegado animationDidStop?
Obrigado, - Batgar
fonte
CAAnimation
'sdelegate
é forte, então talvez seja necessário configurá-lo paranil
evitar reter ciclos!Respostas:
A técnica de Batgar é muito complicada. Por que não tirar vantagem do parâmetro forKey em addAnimation? Foi planejado exatamente para este propósito. Basta realizar a chamada para setValue e mover a string chave para a chamada addAnimation. Por exemplo:
Então, em seu retorno de chamada animationDidStop, você pode fazer algo como:
fonte
anim.removedOnCompletion = NO;
para que ele ainda exista quando-animationDidStop:finished:
for chamado.Acabei de descobrir uma maneira ainda melhor de fazer o código de conclusão para CAAnimations:
Eu criei um typedef para um bloco:
E uma chave que uso para adicionar um bloco a uma animação:
Então, se eu quiser executar o código de conclusão da animação após o término de um CAAnimation, eu me coloco como delegado da animação e adiciono um bloco de código à animação usando setValue: forKey:
Em seguida, implemento um método animationDidStop: completed:, que verifica se há um bloco na chave especificada e o executa se encontrado:
A beleza dessa abordagem é que você pode escrever o código de limpeza no mesmo lugar onde cria o objeto de animação. Melhor ainda, como o código é um bloco, ele tem acesso a variáveis locais no escopo envolvente em que está definido. Você não tem que mexer com a configuração de dicionários userInfo ou outras bobagens, e não tem que escrever um método de animação DidStop: terminado que fica cada vez mais complexo conforme você adiciona diferentes tipos de animações.
Verdade seja dita, CAAnimation deve ter uma propriedade de bloco de conclusão incorporada a ele e suporte do sistema para chamá-lo automaticamente se for especificado. No entanto, o código acima oferece a mesma funcionalidade com apenas algumas linhas de código extra.
fonte
theBlock();
ser invocado e acredito que seja devido ao fato de que o escopo do bloco foi destruído.A segunda abordagem só funcionará se você definir explicitamente sua animação para não ser removida na conclusão antes de executá-la:
Se você não fizer isso, sua animação será removida antes de ser concluída e o retorno de chamada não a encontrará no dicionário.
fonte
Todas as outras respostas são muito complicadas! Por que você simplesmente não adiciona sua própria chave para identificar a animação?
Esta solução é muito fácil, tudo que você precisa é adicionar sua própria chave para a animação (animationID neste exemplo)
Insira esta linha para identificar a animação1 :
e isso para identificar a animação2 :
Teste assim:
Não requer nenhuma variável de instância :
fonte
[animation valueForKey:@"animationID"]
Para tornar explícito o que está implícito acima (e o que me trouxe aqui depois de algumas horas perdidas): não espere ver o objeto de animação original que você alocou devolvido a você por
quando a animação termina, porque
[CALayer addAnimation:forKey:]
faz uma cópia da sua animação.O que você pode confiar é que os valores chaveados que você deu ao seu objeto de animação ainda estão lá com valor equivalente (mas não necessariamente equivalência de ponteiro) no objeto de animação de réplica passado com a
animationDidStop:finished:
mensagem. Conforme mencionado acima, use KVC e você terá amplo escopo para armazenar e recuperar o estado.fonte
[animation setValue:@"myanim" forKey:@"name"]
e pode até definir a camada que está sendo animada usando[animation setValue:layer forKey:@"layer"]
. Esses valores podem então ser recuperados nos métodos delegados.valueForKey:
retornanil
para mim, alguma ideia do porquê?Posso ver a maioria das respostas objc. Farei uma para o swift 2.3 com base na melhor resposta acima.
Para começar, será bom armazenar todas essas chaves em um struct privado para que seja seguro para tipos e alterá-lo no futuro não trará bugs irritantes apenas porque você se esqueceu de alterá-lo em todo o código:
Como você pode ver, mudei os nomes das variáveis / animações para que fique mais claro. Agora definindo essas chaves quando a animação é criada.
(...)
Então, finalmente, lidar com o delegado para quando a animação parar
fonte
IMHO usando o valor-chave da Apple é a maneira elegante de fazer isso: destina-se especificamente a permitir a adição de dados específicos do aplicativo aos objetos.
Outra possibilidade muito menos elegante é armazenar referências a seus objetos de animação e fazer uma comparação de ponteiro para identificá-los.
fonte
Para eu verificar se 2 objetos CABasicAnimation são a mesma animação, eu uso a função keyPath para fazer exatamente isso.
if ([animationA keyPath] == [animationB keyPath])
fonte
Gosto de usar
setValue:forKey
: para manter uma referência da visualização que estou animando, é mais seguro do que tentar identificar de forma única a animação com base no ID, porque o mesmo tipo de animação pode ser adicionado a diferentes camadas.Esses dois são equivalentes:
com este:
e no método delegado:
fonte
Xcode 9 Swift 4.0
Você pode usar os valores-chave para relacionar uma animação adicionada à animação retornada no método de delegado animationDidStop.
Declare um dicionário para conter todas as animações ativas e conclusões relacionadas:
Ao adicionar sua animação, defina uma chave para ela:
Em animationDidStop, a mágica acontece:
fonte