Лучший способ выполнить несколько последовательных анимаций UIView?

У меня есть серия из 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.secondFeedVie,w.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)

ьзованием блоков (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];
 burtlo05 апр. 2012 г., 21:26
Спасибо за этот код.
 lxmfly12323 мая 2016 г., 03:21
Хороший код Тем не менее, блог запрашивает имя пользователя и пароль для входа, но я не вижу ссылки на регистрацию, могу ли я прочитать блог где-нибудь еще?
 Duncan C04 апр. 2013 г., 02:26
Это выглядит как хорошее элегантное решение. (проголосовал) Мне придется проверить проект GitHub.

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

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

 Mark Struzinski04 окт. 2010 г., 00:45
Отличная идея, но мы пока что поддерживаем iOS 3.x как минимум. Хотелось бы, чтобы мы могли. Это решило бы много проблем!

ультат.

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/

 CodeReaper10 сент. 2013 г., 15:19
Вы просто должны любить имя этого URL. :-)
 GoldenJoe19 февр. 2014 г., 01:24
Кажется, это отличное решение, но я не совсем понимаю код. Почему в конце первой строки ставится точка с запятой при каждом добавлении блока в очередь?
 lxmfly12322 мая 2016 г., 03:09
Замечательные блоки и NSMutableArray!
 // 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]; 

 Bobj-C03 окт. 2010 г., 16:44
да верно только 1 анимация
 Mark Struzinski03 окт. 2010 г., 15:39
Каждый вид анимации имеет свое поведение в моем случае. По сути, один скользит, второй скользит и отскакивает от первого, затем третий слегка скользит в поле зрения справа. Код, показанный выше, выполняет одинаковую анимацию для всех представлений, верно?

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

Сохраните его, когда вы установите его. Авто-релиз в начале обратного вызова. Это выглядит неприлично (функции 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];, Это неприглядно (и, вероятно, вызывает неопределенное поведение в соответствии со стандартом Си).

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

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

Ваш ответ на вопрос