Animare lo scorrimento infinito di un’immagine in un loop continuo

Attualmente ho un’immagine di nuvole di 2048×435 che scorre attraverso un UIImageView orientato al paesaggio di 1024×435 usando 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:

sviluppo su Xcode 4.2 per iOS 5 con orientamento paesaggistico ipad ARC non 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]; } 

Dici che la tua immagine è larga 2048 e la tua vista è larga 1024. Non so se questo significa che hai duplicato il contenuto di un’immagine a 1024 di larghezza per creare un’immagine di 2048-larghezza.

Ad ogni modo, ecco cosa suggerisco. Dovremo memorizzare il livello cloud e la sua animazione nelle variabili di istanza:

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

Invece di impostare il contenuto del livello cloud sull’immagine del cloud, abbiamo impostato il colore di sfondo su un colore di motivo creato dall’immagine. In questo modo, possiamo impostare i limiti del livello a qualsiasi cosa vogliamo e l’immagine verrà affiancata 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 colloca l’origine in basso a sinistra anziché in alto a sinistra, con l’asse Y crescente. Ciò significa che il modello sarà disegnato al rovescio. Possiamo sistemarlo girando l’asse Y:

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

Per impostazione predefinita, il punto di ancoraggio di un livello è al 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 nell’angolo in alto a sinistra:

  cloudLayer.anchorPoint = CGPointMake(0, 1); 

La larghezza del livello 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 livello alla vista:

  [self.cloudsImageView.layer addSublayer:cloudLayer]; 

Ora impostiamo 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 che l’angolo in alto a sinistra del livello inizi nell’angolo in alto a sinistra della vista:

  CGPoint startPoint = CGPointZero; 

e vogliamo che l’angolo in alto a sinistra del livello si muova a sinistra della larghezza dell’immagine:

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

Il resto del setup di animazione è lo stesso del tuo codice. Ho modificato la durata a 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 colbind effettivamente l’animazione al livello:

  [self applyCloudLayerAnimation]; } 

Ecco il metodo che applica l’animazione:

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

Quando l’applicazione passa allo sfondo (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 a osservare la notifica che ci informa che l’app è entrata 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 vista scompare o quando il controllore della vista viene deallocato:

 - (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 riceve effettivamente la notifica, dobbiamo applicare nuovamente l’animazione:

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

Ecco tutto il codice insieme per una facile 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. // eg 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]; } 

Rob, fratello rock. 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 chiunque possa finire qui a caccia di una soluzione verticale, questo è quello che ho trovato, modellato su quanto 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"]; } 

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  #import  @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