2012-01-09 89 views
22

目前我有一个2048x435云的图像,使用CABasicAnimation滚动横跨1024x435的Landscaped定向UIImageView。云图像应该滚动,但是我在尝试获取重复的云图像以连接到当前云图像的背面时遇到了一些麻烦,以便在云图像之间没有间隙。我一直在努力寻找解决方案,所以任何帮助将不胜感激。我目前的代码:动画无缝循环中的图像的无限滚动

在Xcode 4.2上针对iOS 5开发,具有ARC非故事板ipad环境定位。

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

如何让这个循环在从后台返回后继续(最小化应用程序)? – Mutawe 2012-11-05 13:40:52

回答

57

你说你的图像是2048宽,你的观点是1024宽。我不知道这是否意味着您已经复制了1024宽度图像的内容以制作2048宽度图像。

无论如何,这是我的建议。我们需要云层和动画保存在实例变量:

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

而不是设置云图层的内容到云端形象,我们设置它的背景颜色从图像创建的图案颜色。这样一来,我们就可以设置为任何我们想要的图层边界和图像将被平铺,填补了界限:

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

然而,CALayer的坐标系统将把原点在左下角,而不是左上角, Y轴增加。这意味着该模式将被颠倒。我们可以解决这个问题通过翻转Y轴:

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); 

动画设置的其余部分与您的代码相同。我改变了持续时间为3秒来进行测试:

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

当应用程序进入后台(因为用户切换到另一个应用程序),系统会从云层中移除动画。所以当我们再次进入前景时我们需要重新附加它。这就是为什么我们有applyCloudLayerAnimation方法。我们需要在应用程序进入前台时调用该方法。

viewDidAppear:,我们就可以开始观测应用已经进入前台,它告诉我们的通知:

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

这里的所有代码放在一起,便于复制和粘贴:

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

这很奇妙!感谢您的深入解释! – r00tb0x 2012-01-09 21:54:40

+0

如何使这个循环在从后台取回后继续(最小化应用程序)? – Mutawe 2012-11-05 13:39:33

+0

@rob mauoff请帮助 – Mutawe 2012-11-06 06:13:33

6

罗布,你摇滚兄弟。我正在寻找一种方法来做同样的事情,但垂直。在阅读上面的答案后,我没有任何问题调整以适应我的需求。对于任何人都可以在这里结束了对他们的追捕垂直解决方案,这是我结束了,上面仿照:

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

与垂直和水平滚动选项原液

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