Подтвердить что ты не робот

Анимация бесконечной прокрутки изображения в бесшовном цикле

В настоящее время у меня есть изображение облаков размером 2048x435, которое прокручивается через ориентированный на ландшафт ориентированный UIImageView 1024x435 с использованием CABasicAnimation. Изображение облака прокручивается так, как должно, однако у меня возникают некоторые проблемы с попыткой получить дублирующее изображение облаков, чтобы подключиться к задней части текущего изображения облаков, чтобы между изображениями облаков не было никакого промежутка. Я изо всех сил пытаюсь найти один день, пытаясь найти решение, поэтому любая помощь будет очень признательна. Мой текущий код:

развивающийся на 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];
}
4b9b3361

Ответ 1

Вы говорите, что ваше изображение имеет ширину 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];
}

Ответ 2

Роб, ты, брат. Я искал способ сделать то же самое, но вертикально. Прочитав ваш ответ выше, у меня не было проблем с его настройкой в ​​соответствии с моими потребностями. Для тех, кто может оказаться здесь на охоте за вертикальным решением, это то, с чем я закончил, смоделированный после вышесказанного:

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

Ответ 3

оригинальное решение с возможностью вертикальной и горизонтальной прокрутки

//
//  originally found here: http://stackoverflow.com/info/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