2010-10-03 2 views
15

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

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

-(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, и я ищу некоторые рекомендации, которые можно применить здесь. Я иду об этом не так?

ответ

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

+0

Каждый вид анимации имеет различное поведение в моем случае. В основном, один слайд, второй слайд и отскакивает от первого в сторону, затем третий слегка скользит вправо справа. Код, показанный выше, выполняет одну и ту же анимацию во всех представлениях, не так ли? –

+0

Да, это правда только 1 анимация –

19

Если вы готовы подойти к прошивке 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: содержит изменения свойств для анимации сразу, а второй - действие, выполняемое по завершении анимации. Поскольку этот обратный вызов может содержать другую анимацию такого типа, вы можете вложить их в произвольные цепочки анимаций.

+0

Отличная идея, но мы готовы поддерживать хотя бы iOS 3.x. Хотелось бы, чтобы мы могли. Это решило бы массу проблем! –

1

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

  • Сохраните его, когда вы его установили. Autorelease это в начале обратного вызова. Это кажется нехорошим (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

Это старый пост. ARC на самом деле дает вам решение этой проблемы (если вы все еще используете старый метод beginAnimations: context: ... commitAnimations. Вы можете использовать __bridge_retained cast вашего NSObject для указателя void *, а затем в методе завершения , используйте __bridge_transfer. Мне нужно было сделать это для некоторых анимаций перехода, которые использовали вызовы старого стиля, и это работает. (Компилятор жалуется, но объект получает и сохраняется должным образом.) –

10

Мы создали компонент для целенаправленных действий анимации с использованием блоков (CPAnimationSequence on Github). Мы описали мотивы и обоснование in our iOS development blog.

Это дает очень читаемый код, что-то вроде этого:

[[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]; 
+0

Благодарим вас за этот код. – burtlo

+0

Это похоже (проголосовав). Мне нужно будет проверить проект github. –

+0

Хороший код. Тем не менее, блог запрашивает имя пользователя и пароль, но я не вижу никакой ссылки для регистрации, могу ли я прочитать блог где-нибудь else? – lxmfly123

16

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

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/

+1

Вам просто нужно любить имя этого URL. :-) – CodeReaper

+2

Это, кажется, отличное решение, но я действительно не понимаю код. Почему точка с запятой в конце первой строки при каждом добавлении блока в очередь? – GoldenJoe

+0

Замечательные блоки и NSMutableArray! – lxmfly123

Смежные вопросы