2012-01-09 8 views
22

Actualmente tengo una imagen de nubes de 2048x435 que se desplaza por un UIImageView orientado al paisaje de 1024x435 con CABasicAnimation. La imagen de la nube se desplaza como debería, sin embargo, estoy teniendo problemas para tratar de obtener una imagen duplicada de las nubes para conectarla a la parte posterior de la imagen actual de las nubes para que no haya espacios entre las imágenes de las nubes. He estado luchando durante casi un día tratando de encontrar una solución, por lo que cualquier ayuda sería muy apreciada. Mi código actual:Anime el desplazamiento infinito de una imagen en un bucle continuo

desarrollando en Xcode 4.2 para iOS 5 con orientación ajardinada ipad ARC sin guiones gráficos.

-(void)cloudScroll 
{ 
    UIImage *cloudsImage = [UIImage imageNamed:@"TitleClouds.png"]; 
    CALayer *cloud = [CALayer layer]; 
    cloud.contents = (id)cloudsImage.CGImage; 
    cloud.bounds = CGRectMake(0, 0, cloudsImage.size.width, cloudsImage.size.height); 
    cloud.position = CGPointMake(self.view.bounds.size.width/2, cloudsImage.size.height/2); 
    [cloudsImageView.layer addSublayer:cloud]; 

    CGPoint startPt = CGPointMake(self.view.bounds.size.width + cloud.bounds.size.width/2, cloud.position.y); 
    CGPoint endPt = CGPointMake(cloud.bounds.size.width/-2, cloud.position.y); 
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"]; 
    anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 
    anim.fromValue = [NSValue valueWithCGPoint:startPt]; 
    anim.toValue = [NSValue valueWithCGPoint:endPt]; 
    anim.repeatCount = HUGE_VALF; 
    anim.duration = 60.0; 
    [cloud addAnimation:anim forKey:@"position"]; 
} 

-(void)viewDidLoad 
{ 
    [self cloudScroll]; 
    [super viewDidLoad]; 
} 
+0

Cómo hacer que este ciclo continúe después de regresar del fondo (Minimizar la aplicación) ?? – Mutawe

Respuesta

57

Usted dice que su imagen es 2048 de ancho y su vista es 1024 de ancho. No sé si esto significa que ha duplicado el contenido de una imagen de 1024 de ancho para hacer una imagen de 2048 de ancho.

De todos modos, esto es lo que sugiero. Vamos a tener que almacenar la capa de nubes y su animación en las variables de instancia:

@implementation ViewController { 
    CALayer *cloudLayer; 
    CABasicAnimation *cloudLayerAnimation; 
} 

En lugar de establecer el contenido de la capa de nubes a la imagen de la nube, establecimos su color de fondo a un color de trama creada a partir de la imagen. De esta manera, podemos establecer límites de la capa a lo que queramos y la imagen será de baldosas para llenar los límites:

-(void)cloudScroll { 
    UIImage *cloudsImage = [UIImage imageNamed:@"TitleClouds.png"]; 
    UIColor *cloudPattern = [UIColor colorWithPatternImage:cloudsImage]; 
    cloudLayer = [CALayer layer]; 
    cloudLayer.backgroundColor = cloudPattern.CGColor; 

Sin embargo, el sistema de coordenadas de un CALayer pone el origen en la esquina inferior izquierda en lugar de la parte superior izquierda, con el eje Y aumentando hacia arriba. Esto significa que el patrón se dibujará al revés. Podemos arreglar esto por voltear el eje Y:

cloudLayer.transform = CATransform3DMakeScale(1, -1, 1); 

Por defecto, punto de anclaje de una capa está en su centro. Esto significa que establecer la posición de la capa establece la posición de su centro. Será más fácil posicionar la capa al establecer la posición de su esquina superior izquierda. Podemos hacer esto mediante el movimiento de su punto de anclaje a su esquina superior izquierda:

cloudLayer.anchorPoint = CGPointMake(0, 1); 

La anchura de la capa debe ser el ancho de la imagen, más la anchura de la vista que contiene. De esta forma, a medida que desplazamos la capa para que se vea el borde derecho de la imagen, se dibujará otra copia de la imagen a la derecha de la primera copia.

CGSize viewSize = self.cloudsImageView.bounds.size; 
    cloudLayer.frame = CGRectMake(0, 0, cloudsImage.size.width + viewSize.width, viewSize.height); 

Ahora estamos listos para añadir la capa a la vista:

[self.cloudsImageView.layer addSublayer:cloudLayer]; 

Ahora vamos a configurar la animación. Recuerde que hemos cambiado el punto de anclaje de la capa, por lo que podemos controlar su posición estableciendo la posición de su esquina superior izquierda. Queremos esquina superior izquierda de la capa para iniciar en la esquina superior izquierda de la vista:

CGPoint startPoint = CGPointZero; 

y queremos esquina superior izquierda de la capa a moverse a la izquierda por el ancho de la imagen:

CGPoint endPoint = CGPointMake(-cloudsImage.size.width, 0); 

El resto de la configuración de animación es la misma que su código.He cambiado la duración de 3 segundos para la prueba:

cloudLayerAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; 
    cloudLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 
    cloudLayerAnimation.fromValue = [NSValue valueWithCGPoint:startPoint]; 
    cloudLayerAnimation.toValue = [NSValue valueWithCGPoint:endPoint]; 
    cloudLayerAnimation.repeatCount = HUGE_VALF; 
    cloudLayerAnimation.duration = 3.0; 

Vamos a llamar a otro método para fijar realmente la animación a la capa:

[self applyCloudLayerAnimation]; 
} 

Aquí está el método que aplica la animación:

- (void)applyCloudLayerAnimation { 
    [cloudLayer addAnimation:cloudLayerAnimation forKey:@"position"]; 
} 

Cuando la aplicación entra en segundo plano (porque el usuario cambió a otra aplicación), el sistema elimina la animación de la capa de la nube. Entonces tenemos que volver a conectarlo cuando volvamos a entrar en primer plano. Es por eso que tenemos el método applyCloudLayerAnimation. Necesitamos llamar a ese método cuando la aplicación entra en primer plano.

En viewDidAppear:, podemos empezar a observar la notificación que nos dice que la aplicación ha entrado en el primer plano:

- (void)viewDidAppear:(BOOL)animated { 
    [super viewDidAppear:animated]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; 
} 

Tenemos que dejar de observar la notificación cuando nuestro punto de vista desaparece, o cuando se cancela la asignación del controlador de vista:

- (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; 
} 

- (void)dealloc { 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; 
} 

Cuando el controlador de vista realmente recibe la notificación, tenemos que aplicar de nuevo la animación:

- (void)applicationWillEnterForeground:(NSNotification *)note { 
    [self applyCloudLayerAnimation]; 
} 

Aquí es todo el código juntos para copiar y pegar fácil:

- (void)viewDidLoad { 
    [self cloudScroll]; 
    [super viewDidLoad]; 
} 

-(void)cloudScroll { 
    UIImage *cloudsImage = [UIImage imageNamed:@"TitleClouds.png"]; 
    UIColor *cloudPattern = [UIColor colorWithPatternImage:cloudsImage]; 
    cloudLayer = [CALayer layer]; 
    cloudLayer.backgroundColor = cloudPattern.CGColor; 

    cloudLayer.transform = CATransform3DMakeScale(1, -1, 1); 

    cloudLayer.anchorPoint = CGPointMake(0, 1); 

    CGSize viewSize = self.cloudsImageView.bounds.size; 
    cloudLayer.frame = CGRectMake(0, 0, cloudsImage.size.width + viewSize.width, viewSize.height); 

    [self.cloudsImageView.layer addSublayer:cloudLayer]; 

    CGPoint startPoint = CGPointZero; 
    CGPoint endPoint = CGPointMake(-cloudsImage.size.width, 0); 
    cloudLayerAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; 
    cloudLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 
    cloudLayerAnimation.fromValue = [NSValue valueWithCGPoint:startPoint]; 
    cloudLayerAnimation.toValue = [NSValue valueWithCGPoint:endPoint]; 
    cloudLayerAnimation.repeatCount = HUGE_VALF; 
    cloudLayerAnimation.duration = 3.0; 
    [self applyCloudLayerAnimation]; 
} 

- (void)applyCloudLayerAnimation { 
    [cloudLayer addAnimation:cloudLayerAnimation forKey:@"position"]; 
} 

- (void)viewDidUnload { 
    [self setCloudsImageView:nil]; 
    [super viewDidUnload]; 
    // Release any retained subviews of the main view. 
    // e.g. self.myOutlet = nil; 
} 

- (void)viewDidAppear:(BOOL)animated { 
    [super viewDidAppear:animated]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; 
} 

- (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; 
} 

- (void)dealloc { 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; 
} 

- (void)applicationWillEnterForeground:(NSNotification *)note { 
    [self applyCloudLayerAnimation]; 
} 
+1

¡Esto funciona maravillosamente! ¡Gracias por las explicaciones en profundidad! – r00tb0x

+0

Cómo hacer que este ciclo continúe después de volver del fondo (Minimizar la aplicación) ?? – Mutawe

+0

@rob mauoff Por favor, ¿pueden ayudarnos – Mutawe

6

Rob, usted oscila hermano. Estaba buscando una forma de hacer lo mismo pero verticalmente. Después de leer su respuesta anterior, no tuve problemas para ajustarla a mis necesidades. Para cualquier persona que puede terminar aquí en su búsqueda de una solución vertical esto es lo que terminó con, el modelo de lo anterior:

- (IBAction)animateBackground6 
{ 
    UIImage *backgroundImage = [UIImage imageNamed:@"space.png"]; 
    UIColor *backgroundPattern = [UIColor colorWithPatternImage:backgroundImage]; 

    CALayer *background = [CALayer layer]; 
    background.backgroundColor = backgroundPattern.CGColor; 
    background.transform = CATransform3DMakeScale(1, -1, 1); 
    background.anchorPoint = CGPointMake(0, 1); 

    CGSize viewSize = self.backgroundImageView.bounds.size; 
    background.frame = CGRectMake(0, 0, viewSize.width, backgroundImage.size.height + viewSize.height); 
    [self.backgroundImageView.layer addSublayer:background]; 

    CGPoint startPoint = CGPointZero; 
    CGPoint endPoint = CGPointMake(0, -backgroundImage.size.height); 

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 
    animation.fromValue = [NSValue valueWithCGPoint:endPoint]; 
    animation.toValue = [NSValue valueWithCGPoint:startPoint]; 
    animation.repeatCount = HUGE_VALF; 
    animation.duration = 5.0; 
    [background addAnimation:animation forKey:@"position"]; 
} 
4

solución original con opciones para el desplazamiento vertical y horizontal

// 
// originally found here: http://stackoverflow.com/questions/8790079/animate-infinite-scrolling-of-an-image-in-a-seamless-loop 
// 

#import <UIKit/UIKit.h> 
#import <QuartzCore/QuartzCore.h> 

@interface TiledCloundScrollViewController : UIViewController { 
    CALayer *cloudLayer; 
    CABasicAnimation *cloudLayerAnimation; 

    UIImage *cloudsImage; 
    BOOL verticalScroll; 
    CFTimeInterval animationDuration; 
} 

- (id) initWithImage:(UIImage*)cloudsImage verticalScroll:(BOOL)verticalScroll animationDuration:(CFTimeInterval)animationDuration; 

@end 



#import "TiledCloundScrollViewController.h" 

@interface TiledCloundScrollViewController() 
@end 

@implementation TiledCloundScrollViewController 

- (id) init { 
    [self doesNotRecognizeSelector:_cmd]; 
    return nil; 
} 

- (id) initWithImage:(UIImage*)image verticalScroll:(BOOL)vScroll animationDuration:(CFTimeInterval)duration { 
    self = [super init]; 
    if (self) { 
     cloudsImage = image; 
     verticalScroll = vScroll; 
     animationDuration = duration; 
    } 
    return self; 
} 

- (void) viewDidLoad { 
    [super viewDidLoad]; 

    self.view.clipsToBounds = YES; 
    const CGSize viewSize = self.view.bounds.size; 
    const CGSize imageSize = cloudsImage.size; 

    UIColor *cloudPattern = [UIColor colorWithPatternImage:cloudsImage]; 
    cloudLayer = [CALayer layer]; 
    cloudLayer.backgroundColor = cloudPattern.CGColor; 
    cloudLayer.transform = CATransform3DMakeScale(1, -1, 1); 
    cloudLayer.anchorPoint = CGPointMake(0, 1); 
    [self.view.layer addSublayer:cloudLayer]; 

    CGPoint startPoint = CGPointZero; 
    CGPoint endPoint; 
    if (verticalScroll) { 
     endPoint = CGPointMake(0, -imageSize.height); 
     cloudLayer.frame = CGRectMake(0, 0, viewSize.width, viewSize.height + imageSize.height); 
    } else { 
     endPoint = CGPointMake(-imageSize.width, 0); 
     cloudLayer.frame = CGRectMake(0, 0, viewSize.width + imageSize.width, viewSize.height); 
    } 

    cloudLayerAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; 
    cloudLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 
    cloudLayerAnimation.fromValue = [NSValue valueWithCGPoint:startPoint]; 
    cloudLayerAnimation.toValue = [NSValue valueWithCGPoint:endPoint]; 
    cloudLayerAnimation.repeatCount = HUGE_VALF; 
    cloudLayerAnimation.duration = animationDuration; 
    [self applyCloudLayerAnimation]; 
} 

- (void) viewDidUnload { 
    cloudLayer = nil; 
    cloudLayerAnimation = nil; 
    [super viewDidUnload]; 
} 

- (void) applyCloudLayerAnimation { 
    [cloudLayer addAnimation:cloudLayerAnimation forKey:@"position"]; 
} 

- (void)applicationWillEnterForeground:(NSNotification *)note { 
    [self applyCloudLayerAnimation]; 
} 

- (void)viewDidAppear:(BOOL)animated { 
    [super viewDidAppear:animated]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; 
} 

- (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; 
} 

- (void)dealloc { 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; 
} 


@end