Pergunta

É possível cancelar uma animação UIView enquanto ele estiver em progresso? Ou eu teria que cair para o nível CA?

i. Eu fiz algo parecido com isso (talvez definir uma ação de animação final também):

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

Mas antes da conclusão de animação e eu recebo o evento de animação terminou, eu quero cancelá-lo (cortá-lo curto). Isso é possível? Pesquisando em torno encontra algumas pessoas fazendo a mesma pergunta sem respostas -. E uma ou duas pessoas especulando que não pode ser feito

Foi útil?

Solução

A forma como eu fazer isso é criar uma nova animação para o seu ponto final. Definir uma duração muito curta e certifique-se de usar o método +setAnimationBeginsFromCurrentState: para começar a partir do estado atual. Quando você configurá-lo para SIM, a animação atual é interrompida. É algo como isto:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

Outras dicas

Use:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];

A maneira mais simples para a paragem todas animações em um ponto de vista particular, imediatamente , é o seguinte:

Fazer a ligação do projeto para QuartzCore.framework. No início do seu código:

#import <QuartzCore/QuartzCore.h>

Agora, quando você quiser parar todas as animações em uma exibição mortos em suas trilhas, dizer o seguinte:

[CATransaction begin];
[theView.layer removeAllAnimations];
[CATransaction commit];

A linha do meio iria funcionar por si só, mas há um atraso até que os acabamentos RunLoop (o "momento redesenho"). Para evitar esse atraso, enrole o comando em um bloco de transação explícita como mostrado. Isso funciona desde há outras mudanças têm sido realizados sobre esta camada no RunLoop atual.

No iOS 4 e maior, usar a opção UIViewAnimationOptionBeginFromCurrentState na segunda animação para cortar o primeiro curta de animação.

Como um exemplo, suponha que você tem uma vista com um indicador de atividade. Deseja a desvanecer-se no indicador de atividade enquanto alguns potencialmente o tempo de atividade consumindo começa, e fade-lo para fora quando a atividade estiver concluída. No código abaixo, a visão com o indicador de atividade em que é chamado activityView.

- (void)showActivityIndicator {
    activityView.alpha = 0.0;
    activityView.hidden = NO;
    [UIView animateWithDuration:0.5
                 animations:^(void) {
                     activityView.alpha = 1.0;
                 }];

- (void)hideActivityIndicator {
    [UIView animateWithDuration:0.5
                 delay:0 options:UIViewAnimationOptionBeginFromCurrentState
                 animations:^(void) {
                     activityView.alpha = 0.0;
                 }
                 completion:^(BOOL completed) {
                     if (completed) {
                         activityView.hidden = YES;
                     }
                 }];
}

Para cancelar uma animação você simplesmente precisa definir a propriedade que está sendo animado, fora da animação UIView. Que vai parar a animação onde quer que seja, e o UIView irá saltar para a configuração que acabou de definir.

Infelizmente para ressuscitar esta resposta, mas em iOS 10 as coisas mudaram um bocado, e agora o cancelamento é possível e você pode até mesmo cancelar graciosamente!

Depois de iOS 10 você pode cancelar animações com UIViewPropertyAnimator !

UIViewPropertyAnimator(duration: 2, dampingRatio: 0.4, animations: {
    view.backgroundColor = .blue
})
animator.stopAnimation(true)

Se você passar verdade ele cancela a animação e ele pára exatamente onde você cancelou. O método de conclusão não será chamado. No entanto, se você passar falso você é responsável por terminar a animação:

animator.finishAnimation(.start)

Você pode terminar sua animação e estadia no estado atual (.Current) ou ir para o estado inicial (.start) ou para o estado final (.end)

A propósito, você pode até mesmo fazer uma pausa e reiniciar mais tarde ...

animator.pauseAnimation()
animator.startAnimation()

Nota: Se você não quer um abrupto cancelamento pode inverter a sua animação ou até mesmo mudar sua animação após a pausa

Se você está animando uma restrição alterando a constante em vez de uma propriedade vista nenhum dos outros métodos de trabalho no iOS 8.

Exemplo animação:

self.constraint.constant = 0;
[self.view updateConstraintsIfNeeded];
[self.view layoutIfNeeded];
[UIView animateWithDuration:1.0f
                      delay:0.0f
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     self.constraint.constant = 1.0f;
                     [self.view layoutIfNeeded];
                 } completion:^(BOOL finished) {

                 }];

Solução:

Você precisa remover as animações a partir das camadas de quaisquer pontos de vista que está sendo afetado pela mudança restrição e suas subcamadas.

[self.constraintView.layer removeAllAnimations];
for (CALayer *l in self.constraintView.layer.sublayers)
{
    [l removeAllAnimations];
}

Se você quiser apenas fazer uma pausa animação / stop sem problemas

self.yourView.layer.speed = 0;

Fonte: Como interromper a animação de uma árvore camada

Nenhuma das opções acima resolvido para mim, mas isso ajudou: A animação UIView define a propriedade imediatamente, então anima. Ele pára a animação quando a camada de apresentação corresponde ao modelo (o conjunto de propriedades).

Eu resolvi meu problema, que era "Quero animar a partir de onde você olha como você aparecer" ( 'você' significa o ponto de vista). Se você quer isso, então:

  1. Adicionar QuartzCore.
  2. CALayer * pLayer = theView.layer.presentationLayer;

definir a posição para a camada de apresentação

Eu uso algumas opções, incluindo UIViewAnimationOptionOverrideInheritedDuration

Mas porque a documentação da Apple é vago, eu não sei se ele realmente substitui as outras animações quando usado, ou apenas repõe temporizadores.

[UIView animateWithDuration:blah... 
                    options: UIViewAnimationOptionBeginFromCurrentState ... 
                    animations: ^ {
                                   theView.center = CGPointMake( pLayer.position.x + YOUR_ANIMATION_OFFSET, pLayer.position.y + ANOTHER_ANIMATION_OFFSET);
                   //this only works for translating, but you get the idea if you wanna flip and scale it. 
                   } completion: ^(BOOL complete) {}];

E isso deve ser uma solução decente para agora.

[UIView setAnimationsEnabled:NO];
// your code here
[UIView setAnimationsEnabled:YES];

Eu tenho o mesmo problema; as APIs não tenho nada para cancelar alguma animação específica. A

+ (void)setAnimationsEnabled:(BOOL)enabled

desativa todas as animações, e, portanto, não funciona para mim. Há duas soluções:

1) fazer o seu objeto animado a subexibição. Então, quando você quiser cancelar as animações para esse ponto de vista, remover a vista ou escondê-lo. Muito simples, mas você precisa para recriar a subexibição sem animações se você precisa mantê-lo à vista.

2) repetir o anim apenas um, e fazer um seletor delegado para reiniciar o anim, se necessário, como este:

-(void) startAnimation {
NSLog(@"startAnim alpha:%f", self.alpha);
[self setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(pulseAnimationDidStop:finished:context:)];
[self setAlpha:0.1];
[UIView commitAnimations];
}

- (void)pulseAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if(hasFocus) {
    [self startAnimation];
} else {
    self.alpha = 1.0;
}
}

-(void) setHasFocus:(BOOL)_hasFocus {
hasFocus = _hasFocus;
if(hasFocus) {
    [self startAnimation];
}
}

Problema com 2) é que há sempre atrasar parar o anim que terminar o ciclo de animação atual.

Espero que isso ajude.

Mesmo se você cancelar a animação nas formas acima de animação didStopSelector ainda corre. Então se você tem estados lógicos em sua aplicação conduzido por animações que você terá problemas. Por esta razão, com as formas descritas acima eu uso a variável de contexto de animações UIView. Se você passar o estado atual do seu programa pela param de contexto para a animação, quando a animação parar sua função didStopSelector pode decidir se ele deve fazer algo ou apenas voltar com base no estado atual e o valor do estado passado como contexto.

Versão Swift da solução de Stephen Darlington

UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.1)
// other animation properties

// set view properties
UIView.commitAnimations()
CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
    self.frame = pLayer.frame;
}];

Para pausar uma animação sem reverter estado para original ou final:

CFTimeInterval paused_time = [myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
myView.layer.speed = 0.0;
myView.layer.timeOffset = paused_time;

Quando eu trabalho com animação UIStackView, além removeAllAnimations() eu preciso definir alguns valores para uma inicial porque removeAllAnimations() pode configurá-los para estado imprevisível. Eu tenho stackView com view1 e view2 dentro, e um ponto de vista deve ser visível e um oculto:

public func configureStackView(hideView1: Bool, hideView2: Bool) {
    let oldHideView1 = view1.isHidden
    let oldHideView2 = view2.isHidden
    view1.layer.removeAllAnimations()
    view2.layer.removeAllAnimations()
    view.layer.removeAllAnimations()
    stackView.layer.removeAllAnimations()
    // after stopping animation the values are unpredictable, so set values to old
    view1.isHidden = oldHideView1 //    <- Solution is here
    view2.isHidden = oldHideView2 //    <- Solution is here

    UIView.animate(withDuration: 0.3,
                   delay: 0.0,
                   usingSpringWithDamping: 0.9,
                   initialSpringVelocity: 1,
                   options: [],
                   animations: {
                    view1.isHidden = hideView1
                    view2.isHidden = hideView2
                    stackView.layoutIfNeeded()
    },
                   completion: nil)
}

Nenhuma das soluções respondidas funcionou para mim. Eu resolvi meus problemas desta forma (não sei se é uma maneira correta?), Porque eu tive problemas ao chamar este demasiado rápido (quando a animação anterior ainda não foi concluída). Eu passar minha animação queria com customAnim bloco.

extension UIView
{

    func niceCustomTranstion(
        duration: CGFloat = 0.3,
        options: UIView.AnimationOptions = .transitionCrossDissolve,
        customAnim: @escaping () -> Void
        )
    {
        UIView.transition(
            with: self,
            duration: TimeInterval(duration),
            options: options,
            animations: {
                customAnim()
        },
            completion: { (finished) in
                if !finished
                {
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    self.layer.removeAllAnimations()
                    customAnim()
                }
        })

    }

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top