Лучший способ выполнить несколько последовательных анимаций UIView? - PullRequest
15 голосов
/ 03 октября 2010

У меня есть серия из 8 анимаций UIView, которые появляются сразу после загрузки моего представления.Прямо сейчас я выполняю это, используя метод делегата animationDidStop:finished:context, и все работает как положено.Проблема в том, что у меня есть новый метод для каждой анимации.Большая часть кода в каждом из этих методов повторяется, с изменением только продолжительности анимации и фактического расположения элементов.

Я попытался создать единственный метод, который будет вызываться, и контекст будет содержать необходимые параметрыизменить пользовательский интерфейс соответствующим образом, но он, кажется, рекурсивно вызывает себя намного позже того количества, которое я называю:

-(void)animationDidStop:(NSString *)animationID finished:(BOOL)finished context:(void *)context{
    NSNumber *number = (NSNumber *)context;
    int animationStep = [number intValue];
    int nextAnimationStep = animationStep + 1;
    NSNumber *nextAnimationStepNumber = [NSNumber numberWithInt:nextAnimationStep];
    NSLog(@"Animation Step: %i", animationStep);

    CGRect firstFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.secondFeedView.view.frame.size.width, self.secondFeedView.view.frame.size.height);
    CGRect thirdFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.thirdFeedView.view.frame.size.width, self.thirdFeedView.view.frame.size.height);

    [UIView beginAnimations:nil context:nextAnimationStepNumber];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
    [UIView setAnimationDelegate:self];

    if (animationStep < 8) 
        [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    NSLog(@"Beginning animations");

    switch (animationStep) {
        case 0:
            [UIView setAnimationDuration:.3];
            self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x + 30, self.firstFeedView.view.center.y);
            break;
        case 1:
            [UIView setAnimationDuration:.3];
            self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x  - 30, self.firstFeedView.view.center.y);
            break;
        case 2:
            [UIView setAnimationDuration:.3];
            [self.secondFeedView.view setFrame:firstFrame];
            break;
        case 3:
            [UIView setAnimationDuration:.3];
            self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x + 30, self.firstFeedView.view.center.y);
            break;
        case 4:
            [UIView setAnimationDuration:.3];
            self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 30, self.firstFeedView.view.center.y);
            break;
        case 5:
            NSLog(@"Animation step 6");
            [UIView setAnimationDuration:.5];
            self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x - 230, self.firstFeedView.view.center.y);
            self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 230, self.firstFeedView.view.center.y);
            break;
        case 6:
            [UIView setAnimationDuration:.5];
            [self.thirdFeedView.view setFrame:thirdFrame];
            break;
        case 7:
            [UIView setAnimationDuration:.3];
            self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x + 30, self.firstFeedView.view.center.y);
            break;
        case 8:
            [UIView setAnimationDuration:.3];
            self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x - 30, self.thirdFeedView.view.center.y);
            break;
        default:
            break;
    }

    [UIView commitAnimations];  
}

Я знаю, что это, вероятно, наивная реализация.Я новичок в разработке для iPhone, и я ищу некоторые лучшие практики, чтобы применить здесь.Я поступаю неправильно?

Ответы [ 5 ]

19 голосов
/ 04 октября 2010

Если вы хотите перейти на iOS 4.0, новый подход к анимации на основе блоков может сделать эту анимацию цепочкой тривиальной.Например:

[UIView animateWithDuration:1.0 animations:^{ view.position = CGPointMake(0.0f, 0.0f); } completion:^(BOOL finished){
    [UIView animateWithDuration:0.2 animations:^{ view.alpha = 0.0; } completion:^(BOOL finished){
        [view removeFromSuperview]; }];
}]

заставит view анимироваться в (0, 0) в течение 1 секунды, исчезнет на 0,2 секунды, а затем будет удален из своего супервизора.Эти анимации будут последовательными.

Первый блок в методе класса +animateWithDuration:animations:completion: содержит сразу свойство, изменяющее свойство animate, а второе - это действие, выполняемое по завершении анимации.Поскольку этот обратный вызов может содержать другую анимацию такого рода, вы можете вкладывать их в произвольные цепочки анимаций.

16 голосов
/ 03 апреля 2013

Вы можете использовать блоки для этой цели и получить очень чистый результат.

NSMutableArray* animationBlocks = [NSMutableArray new];

typedef void(^animationBlock)(BOOL);

// getNextAnimation
// removes the first block in the queue and returns it
animationBlock (^getNextAnimation)() = ^{
    animationBlock block = (animationBlock)[animationBlocks firstObject];
    if (block){
        [animationBlocks removeObjectAtIndex:0];
        return block;
    }else{
         return ^(BOOL finished){};
    }
};

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    [UIView animateWithDuration:1.0 animations:^{
        //...animation code...
    } completion: getNextAnimation()];
}];

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    [UIView animateWithDuration:1.0 animations:^{
        //...animation code...
    } completion: getNextAnimation()];
}];

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    NSLog(@"Multi-step Animation Complete!");
}];

// execute the first block in the queue
getNextAnimation()(YES);

Взято из: http://xibxor.com/objective-c/uiview-animation-without-nested-hell/

10 голосов
/ 28 июля 2011

Мы создали компонент для декларирования цепочки шагов анимации с использованием блоков ( CPAnimationSequence на Github ).Мы описали мотивы и обоснование в нашем блоге по разработке для iOS .

. Он дает вам очень читабельный код, примерно такой:

[[CPAnimationSequence sequenceWithSteps:
    [CPAnimationStep           for:0.25 animate:^{ self.imageView.alpha = 0.0; }],
    [CPAnimationStep           for:0.25 animate:^{ self.headline.alpha = 0.0;  }],
    [CPAnimationStep           for:0.25 animate:^{ self.content.alpha = 0.0;   }],
    [CPAnimationStep after:1.0 for:0.25 animate:^{ self.headline.alpha = 1.0;  }],
    [CPAnimationStep           for:0.25 animate:^{ self.content.alpha = 1.0;   }],
    nil]
runAnimated:YES];
1 голос
/ 04 октября 2010

«Контекст» является пустым * и поэтому не сохраняется.Есть несколько вариантов:

  • Сохраните его при его настройке.Авто-релиз в начале обратного вызова.Это выглядит странно (функции Objective C должны сохранить / освободить соответствующим образом, но, к сожалению, контекст не является id).
  • Сохраните число в строке: int animationStep = [animationID intValue];и [UIView beginAnimations:[NSString stringWithFormat:@"%d", nextAnimationStep] context:NULL];.Это также кажется странным.
  • Предполагая, что UIKit / CoreAnimation не разыменовывает указатель, int animationStep = (int)context; и [UIView beginAnimations:nil context:(void*)nextAnimationStep];.Это странно (и, вероятно, вызывает неопределенное поведение в соответствии со стандартом C.)

С другой стороны, finished:(BOOL)finished должно быть finished:(NSNumber*)finished.Они сделали ошибку в оригинальных документах;по-видимому, было проще изменить документы, чтобы они отражали API, а не менять API и магически поддерживать обратную совместимость (даже если прохождение bool более разумно).

0 голосов
/ 03 октября 2010
 // create the view that will execute our animation
 UIImageView* logo = [[UIImageView alloc] initWithFrame:self.view.frame];
 // load all the frames of our animation
 logo.animationImages = [NSArray arrayWithObjects:  
                                 [UIImage imageNamed:@"frame_00001.png"],

                         [UIImage imageNamed:@"frame_00002.png"],
                         [UIImage imageNamed:@"frame_00003.png"],
                         [UIImage imageNamed:@"frame_00004.png"],
                                 [UIImage imageNamed:@"frame_00005"], nil];

 // all frames will execute in 3 seconds
 logo.animationDuration =3.3;
 // repeat the annimation forever
logo.animationRepeatCount = 1;
 // start animating
 [logo startAnimating];
 // add the animation view to the main window 
 [self.view addSubview:logo];
 [logo release]; 

поместите это в ViewDidLoad

...