2012-01-09 16 views
22

Attualmente ho un'immagine di nuvole di 2048x435 che scorre su un UIImageView orientato al paesaggio di 1024x435 utilizzando CABasicAnimation. L'immagine del cloud scorre come dovrebbe, tuttavia ho qualche problema nel tentativo di ottenere un'immagine di nuvole duplicate per connettersi alla parte posteriore dell'immagine delle nuvole corrente in modo che non ci sia spazio tra le immagini delle nuvole. Ho lottato per circa un giorno cercando di trovare una soluzione, quindi qualsiasi aiuto sarebbe molto apprezzato. Il mio codice attuale:Animare lo scorrimento infinito di un'immagine in un loop continuo

in via di sviluppo su Xcode 4.2 per iOS 5 con orientamento paesaggistico ipad ARC non di storyboard.

-(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

Come fare in modo che questo ciclo continui dopo essere tornato dallo sfondo (minimizzare l'applicazione) ?? – Mutawe

risposta

57

Si dice che l'immagine è larga 2048 e la vista è 1024 di larghezza. Non so se questo significa che hai duplicato il contenuto di un'immagine a 1024 di larghezza per creare un'immagine di 2048-larghezza.

In ogni caso, ecco cosa suggerisco. Avremo bisogno di memorizzare lo strato di nubi e la sua animazione in variabili di istanza:

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

Invece di impostare contenuto del livello cloud per l'immagine nuvola, impostiamo il colore di sfondo a un colore modello creato dall'immagine. In questo modo, siamo in grado di impostare limiti del livello di quello che vogliamo e l'immagine verrà piastrelle per riempire i limiti:

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

Tuttavia, il sistema di coordinate di un CALayer pone l'origine in basso a sinistra invece che in alto a sinistra, con l'asse Y crescente. Ciò significa che il modello sarà disegnato al rovescio. Possiamo risolvere che girando l'asse Y:

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

Per impostazione predefinita, il punto di ancoraggio di un livello è al suo centro. Ciò significa che l'impostazione della posizione del livello imposta la posizione del suo centro. Sarà più semplice posizionare il livello impostando la posizione del suo angolo superiore sinistro. Possiamo farlo spostando il suo punto di ancoraggio per l'angolo superiore sinistro:

cloudLayer.anchorPoint = CGPointMake(0, 1); 

La larghezza dello strato deve essere la larghezza dell'immagine più la larghezza della vista contenente. In questo modo, mentre scorriamo lo strato in modo che il bordo destro dell'immagine venga visualizzato, un'altra copia dell'immagine verrà disegnata a destra della prima copia.

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

Ora siamo pronti per aggiungere il layer alla vista:

[self.cloudsImageView.layer addSublayer:cloudLayer]; 

Ora diamo istituito l'animazione. Ricorda che abbiamo cambiato il punto di ancoraggio del livello, in modo che possiamo controllarne la posizione impostando la posizione del suo angolo superiore sinistro. Vogliamo nell'angolo in alto a sinistra del livello per iniziare a nell'angolo superiore sinistro della vista:

CGPoint startPoint = CGPointZero; 

e vogliamo angolo superiore sinistro del livello per spostarsi a sinistra per la larghezza dell'immagine:

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

Il resto del setup di animazione è lo stesso del tuo codice.Ho cambiato la durata di 3 secondi per il test:

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; 

Chiameremo un altro metodo per attaccare in realtà l'animazione allo strato:

[self applyCloudLayerAnimation]; 
} 

Ecco il metodo che si applica l'animazione:

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

Quando l'applicazione passa in background (poiché l'utente passa a un'altra app), il sistema rimuove l'animazione dal livello cloud. Quindi dobbiamo riattaccarlo quando entriamo di nuovo in primo piano. Ecco perché abbiamo il metodo applyCloudLayerAnimation. Dobbiamo chiamare questo metodo quando l'app passa in primo piano.

In viewDidAppear:, possiamo iniziare osservando la notifica che ci dice l'applicazione è entrato in primo piano:

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

Dobbiamo smettere di osservare la notifica quando la nostra visione scompare, o quando il controller della vista è deallocata:

- (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]; 
} 

Quando il controller della vista in realtà riceve la notifica, abbiamo bisogno di applicare di nuovo l'animazione:

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

Ecco tutto il codice per semplificarne la copia e incolla:

- (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

Questo funziona meravigliosamente! Grazie per le spiegazioni approfondite! – r00tb0x

+0

Come rendere questo ciclo per continuare dopo essere tornato dallo sfondo (Ridurre l'applicazione)? – Mutawe

+0

@rob mauoff Si prega di aiutare – Mutawe

6

Rob, you rock fratello. Stavo cercando un modo per fare la stessa cosa, ma in verticale. Dopo aver letto la risposta sopra, non ho avuto problemi ad adattarlo alle mie esigenze. Per tutti coloro che possono finire qui sul loro caccia di una soluzione verticale questo è ciò che ho finito con, sul modello di cui sopra:

- (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

soluzione originale con opzioni per scorrimento verticale e orizzontale

// 
// 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