在无缝循环中动画无限滚动图像

r00*_*b0x 23 xcode cocoa-touch objective-c ipad

目前我有一张2048x435云的图像,使用CABasicAnimation滚动横跨面向大陆的UIDmageView 1024x435.云图像应该滚动,但是我在尝试获取重复的云图像以连接到当前云图像的背面时遇到了一些麻烦,因此云图像之间没有间隙.我一直在努力寻找一个解决方案,所以任何帮助都会非常感激.我目前的代码:

开发Xcode 4.2 for 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];
}
Run Code Online (Sandbox Code Playgroud)

rob*_*off 58

你说你的图像是2048宽,你的视图是1024宽.我不知道这是否意味着您复制了1024幅图像的内容以制作2048幅图像.

无论如何,这是我的建议.我们需要将云层及其动画存储在实例变量中:

@implementation ViewController {
    CALayer *cloudLayer;
    CABasicAnimation *cloudLayerAnimation;
}
Run Code Online (Sandbox Code Playgroud)

我们不是将云层的内容设置为云图像,而是将其背景颜色设置为从图像创建的图案颜色.这样,我们可以将图层的边界设置为我们想要的任何值,图像将平铺以填充边界:

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

但是,CALayer的坐标系将原点放在左下方而不是左上方,Y轴增加.这意味着该图案将被颠倒绘制.我们可以通过翻转Y轴来解决这个问题:

    cloudLayer.transform = CATransform3DMakeScale(1, -1, 1);
Run Code Online (Sandbox Code Playgroud)

默认情况下,图层的锚点位于其中心.这意味着设置图层的位置会设置其中心的位置.通过设置左上角的位置来定位图层会更容易.我们可以通过将其锚点移动到左上角来实现:

    cloudLayer.anchorPoint = CGPointMake(0, 1);
Run Code Online (Sandbox Code Playgroud)

图层的宽度需要是图像的宽度加上包含视图的宽度.这样,当我们滚动图层以使图像的右边缘进入视图时,图像的另一个副本将被绘制到第一个副本的右侧.

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

现在我们准备将图层添加到视图中:

    [self.cloudsImageView.layer addSublayer:cloudLayer];
Run Code Online (Sandbox Code Playgroud)

现在让我们设置动画.请记住,我们更改了图层的锚点,因此我们可以通过设置其左上角的位置来控制其位置.我们希望图层的左上角从视图的左上角开始:

    CGPoint startPoint = CGPointZero;
Run Code Online (Sandbox Code Playgroud)

我们希望图层的左上角向左移动图像的宽度:

    CGPoint endPoint = CGPointMake(-cloudsImage.size.width, 0);
Run Code Online (Sandbox Code Playgroud)

动画设置的其余部分与您的代码相同.我将持续时间更改为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;
Run Code Online (Sandbox Code Playgroud)

我们将调用另一种方法将动画实际附加到图层:

    [self applyCloudLayerAnimation];
}
Run Code Online (Sandbox Code Playgroud)

这是应用动画的方法:

- (void)applyCloudLayerAnimation {
    [cloudLayer addAnimation:cloudLayerAnimation forKey:@"position"];
}
Run Code Online (Sandbox Code Playgroud)

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

viewDidAppear:,我们可以开始观察通知,告诉我们应用程序已进入前台:

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

我们需要在视图消失或视图控制器被取消分配时停止观察通知:

- (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];
}
Run Code Online (Sandbox Code Playgroud)

当视图控制器实际收到通知时,我们需要再次应用动画:

- (void)applicationWillEnterForeground:(NSNotification *)note {
    [self applyCloudLayerAnimation];
}
Run Code Online (Sandbox Code Playgroud)

这里是所有代码,便于复制和粘贴:

- (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];
}
Run Code Online (Sandbox Code Playgroud)

  • @Mutawe我已经修改了我的答案来处理你的问题. (2认同)

Del*_*D0D 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"];
}
Run Code Online (Sandbox Code Playgroud)