2010-10-03 24 views
15

Tengo una serie de 8 animaciones UIView que se producen directamente después de cargar mi vista. En este momento, estoy logrando esto mediante el uso del método de delegado animationDidStop:finished:context, y todo funciona como se espera. El problema es que tengo un nuevo método para cada animación. La mayor parte del código en cada uno de estos métodos se repite, y solo cambian la duración de la animación y el posicionamiento real de los elementos.¿La mejor manera de realizar varias animaciones secuenciales de UIView?

Intenté crear un único método que se llama y tienen el contexto mantener los parámetros necesarios para cambiar la interfaz de usuario adecuada, pero parece ser recursiva que se hace llamar así más allá de la cantidad de veces que llamo:

-(void)animationDidStop:(NSString *)animationID finished:(BOOL)finished context:(void *)context{ 
    NSNumber *number = (NSNumber *)context; 
    int animationStep = [number intValue]; 
    int nextAnimationStep = animationStep + 1; 
    NSNumber *nextAnimationStepNumber = [NSNumber numberWithInt:nextAnimationStep]; 
    NSLog(@"Animation Step: %i", animationStep); 

    CGRect firstFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.secondFeedView.view.frame.size.width, self.secondFeedView.view.frame.size.height); 
    CGRect thirdFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.thirdFeedView.view.frame.size.width, self.thirdFeedView.view.frame.size.height); 

    [UIView beginAnimations:nil context:nextAnimationStepNumber]; 
    [UIView setAnimationBeginsFromCurrentState:YES]; 
    [UIView setAnimationCurve:UIViewAnimationCurveLinear]; 
    [UIView setAnimationDelegate:self]; 

    if (animationStep < 8) 
     [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)]; 

    NSLog(@"Beginning animations"); 

    switch (animationStep) { 
     case 0: 
      [UIView setAnimationDuration:.3]; 
      self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x + 30, self.firstFeedView.view.center.y); 
      break; 
     case 1: 
      [UIView setAnimationDuration:.3]; 
      self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x - 30, self.firstFeedView.view.center.y); 
      break; 
     case 2: 
      [UIView setAnimationDuration:.3]; 
      [self.secondFeedView.view setFrame:firstFrame]; 
      break; 
     case 3: 
      [UIView setAnimationDuration:.3]; 
      self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x + 30, self.firstFeedView.view.center.y); 
      break; 
     case 4: 
      [UIView setAnimationDuration:.3]; 
      self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 30, self.firstFeedView.view.center.y); 
      break; 
     case 5: 
      NSLog(@"Animation step 6"); 
      [UIView setAnimationDuration:.5]; 
      self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x - 230, self.firstFeedView.view.center.y); 
      self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 230, self.firstFeedView.view.center.y); 
      break; 
     case 6: 
      [UIView setAnimationDuration:.5]; 
      [self.thirdFeedView.view setFrame:thirdFrame]; 
      break; 
     case 7: 
      [UIView setAnimationDuration:.3]; 
      self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x + 30, self.firstFeedView.view.center.y); 
      break; 
     case 8: 
      [UIView setAnimationDuration:.3]; 
      self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x - 30, self.thirdFeedView.view.center.y); 
      break; 
     default: 
      break; 
    } 

    [UIView commitAnimations]; 
} 

Sé que esta es probablemente una implementación ingenua. Soy nuevo en el desarrollo de iPhone, y estoy buscando algunas mejores prácticas para aplicar aquí. ¿Voy por esto de la manera incorrecta?

Respuesta

-1
// create the view that will execute our animation 
UIImageView* logo = [[UIImageView alloc] initWithFrame:self.view.frame]; 
// load all the frames of our animation 
logo.animationImages = [NSArray arrayWithObjects: 
           [UIImage imageNamed:@"frame_00001.png"], 

         [UIImage imageNamed:@"frame_00002.png"], 
         [UIImage imageNamed:@"frame_00003.png"], 
         [UIImage imageNamed:@"frame_00004.png"], 
           [UIImage imageNamed:@"frame_00005"], nil]; 

// all frames will execute in 3 seconds 
logo.animationDuration =3.3; 
// repeat the annimation forever 
logo.animationRepeatCount = 1; 
// start animating 
[logo startAnimating]; 
// add the animation view to the main window 
[self.view addSubview:logo]; 
[logo release]; 

poner esto en el viewDidLoad

+0

Cada vista de la animación tiene un comportamiento diferente en mi caso. Básicamente, uno se desliza hacia adentro, el segundo se desliza y rebota el primero fuera del camino, luego el tercero se desliza ligeramente a la vista hacia la derecha. El código que muestra arriba realiza la misma animación en todas las vistas, ¿verdad? –

+0

sí, eso es correcto solo 1 animación –

19

Si usted está dispuesto a intensificar a iOS 4.0, el método de animación basada en bloques nuevos puede hacer esta animación encadenamiento trivial. Por ejemplo:

[UIView animateWithDuration:1.0 animations:^{ view.position = CGPointMake(0.0f, 0.0f); } completion:^(BOOL finished){ 
    [UIView animateWithDuration:0.2 animations:^{ view.alpha = 0.0; } completion:^(BOOL finished){ 
     [view removeFromSuperview]; }]; 
}] 

causará view para animar a (0, 0) más de una duración de 1 segundo, Apagado durante 0,2 segundos, a continuación, ser retirado de su supervista. Estas animaciones serán secuenciales.

El primer bloque en el método de clase +animateWithDuration:animations:completion: contiene los cambios de propiedad para animar a la vez, y el segundo es la acción que se realizará al finalizar la animación. Como esta devolución de llamada puede contener otra animación de este tipo, puede anidarla para crear cadenas arbitrarias de animaciones.

+0

Una gran idea, pero estamos comprometidos a soportar al menos iOS 3.x por el momento. Ojalá pudiéramos, sin embargo. ¡Esto resolvería muchos problemas! –

1

El "contexto" es un vacío *, y por lo tanto no se conserva. Hay algunas opciones:

  • Conservarlo cuando lo configures. Autorelease al comienzo de la devolución de llamada. Esto se siente asqueroso (las funciones de Objective-C deberían retener/liberar adecuadamente, pero desafortunadamente el contexto no es id).
  • Almacene el número en la cadena: int animationStep = [animationID intValue]; y [UIView beginAnimations:[NSString stringWithFormat:@"%d", nextAnimationStep] context:NULL];. Esto también se siente asqueroso.
  • Suponiendo que UIKit/CoreAnimation no desreferencia el puntero, int animationStep = (int)context; y [UIView beginAnimations:nil context:(void*)nextAnimationStep];. Esto es asqueroso (y probablemente causa un comportamiento indefinido de acuerdo con el estándar C).

En otra nota, finished:(BOOL)finished debe ser finished:(NSNumber*)finished. Cometieron un error en los documentos originales; era presumiblemente más fácil cambiar los documentos para reflejar la API en lugar de cambiar la API y tener que mantener mágicamente la compatibilidad con versiones anteriores (aunque pasar un bool es más sensato).

+0

Esta es una publicación anterior. ARC en realidad le da una solución a este problema (si todavía está utilizando el viejo estilo beginAnimations: context: ... commitAnimations method. Puede usar un __bridge_retained cast de su NSObject en un puntero void *, y luego en su método de finalización , uso __bridge_transfer. Necesitaba hacer esto para algunas animaciones de transición que usaban las llamadas antiguas y funciona. (El compilador se queja, pero el objeto se conserva y se libera correctamente.) –

10

Creamos un componente para encadenar pasos de animación de manera declarativa mediante bloques (CPAnimationSequence on Github). Describimos las motivaciones y el fundamento in our iOS development blog.

que le da un código muy legible, algo como esto:

[[CPAnimationSequence sequenceWithSteps: 
    [CPAnimationStep   for:0.25 animate:^{ self.imageView.alpha = 0.0; }], 
    [CPAnimationStep   for:0.25 animate:^{ self.headline.alpha = 0.0; }], 
    [CPAnimationStep   for:0.25 animate:^{ self.content.alpha = 0.0; }], 
    [CPAnimationStep after:1.0 for:0.25 animate:^{ self.headline.alpha = 1.0; }], 
    [CPAnimationStep   for:0.25 animate:^{ self.content.alpha = 1.0; }], 
    nil] 
runAnimated:YES]; 
+0

Gracias por este código. – burtlo

+0

Eso se ve como una buena solución elegante. (votado) Tendré que consultar el proyecto github. –

+0

Buen código. Sin embargo, el blog solicita un nombre de usuario y contraseña, pero no veo ningún enlace de registro, ¿puedo leer el blog en alguna parte? ¿más? – lxmfly123

16

Usted puede utilizar bloques para este propósito y obtener un resultado muy limpia.

NSMutableArray* animationBlocks = [NSMutableArray new]; 

typedef void(^animationBlock)(BOOL); 

// getNextAnimation 
// removes the first block in the queue and returns it 
animationBlock (^getNextAnimation)() = ^{ 
    animationBlock block = (animationBlock)[animationBlocks firstObject]; 
    if (block){ 
     [animationBlocks removeObjectAtIndex:0]; 
     return block; 
    }else{ 
     return ^(BOOL finished){}; 
    } 
}; 

//add a block to our queue 
[animationBlocks addObject:^(BOOL finished){; 
    [UIView animateWithDuration:1.0 animations:^{ 
     //...animation code... 
    } completion: getNextAnimation()]; 
}]; 

//add a block to our queue 
[animationBlocks addObject:^(BOOL finished){; 
    [UIView animateWithDuration:1.0 animations:^{ 
     //...animation code... 
    } completion: getNextAnimation()]; 
}]; 

//add a block to our queue 
[animationBlocks addObject:^(BOOL finished){; 
    NSLog(@"Multi-step Animation Complete!"); 
}]; 

// execute the first block in the queue 
getNextAnimation()(YES); 

Tomado de: http://xibxor.com/objective-c/uiview-animation-without-nested-hell/

+1

Sólo tiene que amar el nombre de esa url. :-) – CodeReaper

+2

Esto parece ser una excelente solución, pero realmente no entiendo el código. ¿Por qué hay un punto y coma al final de la primera línea cada vez que agrega un bloque a la cola? – GoldenJoe

+0

¡Bloques maravillosos y NSMutableArray! – lxmfly123

Cuestiones relacionadas