2012-01-09 14 views
22

Obecnie mam obraz chmur o rozdzielczości 2048x435, przewijający się przez UllmageView zorientowany na krajobraz o rozdzielczości 1024x435 przy użyciu CABasicAnimation. Obraz w chmurze przewija się tak, jak powinien, jednak mam problem z próbą uzyskania zduplikowanego obrazu w chmurach, aby połączyć się z odwrotnością obecnego obrazu chmur, aby nie było luki między obrazami chmur. Od pewnego dnia staram się znaleźć rozwiązanie, więc każda pomoc będzie bardzo cenna. Mój aktualny kod:Animować nieskończone przewijanie obrazu w płynnej pętli

rozwija się na Xcode 4.2 dla iOS 5 z orientacją orientalną ARP i bez storyboardu.

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

Jak aby ta pętla była kontynuowana po powrocie z tła (Zminimalizowanie aplikacji)? – Mutawe

Odpowiedz

57

Mówisz, że twój obraz ma 2048 szerokości, a twój widok ma 1024 szerokości. Nie wiem, czy oznacza to, że powielono zawartość obrazu o szerokości 1024 cm, aby uzyskać obraz o szerokości 2048 cm.

W każdym razie, oto co proponuję. Musimy zapamiętać warstwę chmur i jego animacji w zmiennych instancji:

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

Zamiast ustawiać jego zawartości warstwy chmur jest do chmury obrazu, możemy ustawić jego kolor tła na kolor wzoru utworzonego z obrazka. W ten sposób możemy ustawić granice warstwy, aby cokolwiek chcemy i obraz zostanie wyłożone do wypełnienia granic:

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

Jednak układ współrzędnych danego CALayer stawia pochodzenie w lewym dolnym zamiast górnym rogu przy zwiększaniu się osi Y. Oznacza to, że wzór zostanie odwrócony do góry nogami. Możemy to naprawić, odwracając oś Y:

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

Domyślnie punkt kontrolny warstwy znajduje się w jej środku. Oznacza to, że ustawienie położenia warstwy ustawia położenie jej środka. Łatwiej będzie ustawić warstwę, ustawiając jej górny lewy róg. Możemy to zrobić poprzez przeniesienie jego punktu zaczepienia jej lewym górnym rogu:

cloudLayer.anchorPoint = CGPointMake(0, 1); 

Szerokość warstwy musi być szerokość obrazu plus szerokość widoku zawierającego. W ten sposób, gdy przewijamy warstwę tak, aby widoczna była prawą krawędź obrazu, kolejna kopia obrazu zostanie narysowana po prawej stronie pierwszej kopii.

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

Teraz jesteśmy gotowi, aby dodać warstwę do widoku:

[self.cloudsImageView.layer addSublayer:cloudLayer]; 

Teraz skonfigurować animację. Pamiętaj, że zmieniliśmy punkt zakotwiczenia warstwy, dzięki czemu możemy kontrolować jego położenie, ustawiając pozycję jego lewego górnego rogu. Chcemy lewym górnym rogu warstwy, aby rozpocząć w widoku w lewym górnym rogu:

CGPoint startPoint = CGPointZero; 

i chcemy lewym górnym rogu warstwy, aby przejść w lewo przez szerokość obrazka:

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

Reszta konfiguracji animacji jest taka sama, jak kod.Zmieniłem czas trwania do 3 sekund do testowania:

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; 

Będziemy wywołać inną metodę rzeczywiście załączyć animację do warstwy:

[self applyCloudLayerAnimation]; 
} 

Oto metoda, która ma zastosowanie animacji:

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

Gdy aplikacja wchodzi w tło (ponieważ użytkownik przełączył się na inną aplikację), system usuwa animację z warstwy chmury. Musimy więc ponownie go podłączyć, gdy ponownie wejdziemy na pierwszy plan. Dlatego mamy metodę applyCloudLayerAnimation. Musimy wywołać tę metodę, gdy aplikacja wejdzie na pierwszy plan.

W viewDidAppear:, możemy rozpocząć obserwację powiadomienie, że mówi nam aplikacja wprowadził nową wiedzę:

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

Musimy przestać obserwując powiadomienie, gdy nasz pogląd znika, lub gdy kontroler widoku jest zwalniane:

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

Kiedy kontroler widok rzeczywiście otrzymał zawiadomienie, musimy ponownie zastosować animację:

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

Oto cały kod razem do łatwego kopiowania i wklejania:

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

To działa cudownie! Dziękuję za szczegółowe wyjaśnienia! – r00tb0x

+0

Jak sprawić, by ta pętla była kontynuowana po powrocie z tła (Zminimalizowanie aplikacji)? – Mutawe

+0

@rob mauoff Proszę pomóżcie – Mutawe

6

Rob, You Rock brata. Szukałem sposobu, aby zrobić to samo, ale w pionie. Po przeczytaniu powyższej odpowiedzi nie miałem problemu z dostosowaniem jej do moich potrzeb. Dla każdego, kto może skończyć się tutaj na ich polowanie na pionowej rozwiązania jest to, co skończyło się, wzorowane powyższego:

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

oryginalne rozwiązanie z opcji dla pionowego i poziomego przewijania

// 
// 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 
Powiązane problemy