В UIView, как я могу связать много анимаций друг с другом и заставить их запускаться последовательно

Точнее у меня есть UIScrollView со встроенным UIImageView,

У меня есть объект, который описывает кадр "анимация"

@class AnimationFrame

@property (nonatomic,strong) UIImage *image;
@property (nonatomic) CGRect zoomToRectInImage;
@property (nonatomic) CGFloat duration;

@end

Я хотел бы связать кадры друг с другом таким образом, чтобы представление изображения меняло текущее изображение на animationframe.image и затем увеличьте масштаб до этой позиции.

Я думал о том, чтобы сделать что-то вроде этого (псевдокод):

-(void)animateImageView
{
      // imagine these were created before hand
      NSArray *myAnimationFrames = @[frame1,frame2,frame3,frame4,frame5]; 

      // now iterate through the frames and start the animation
      for (AnimationFrame *frame in myAnimationFrames)
      {
          self.scrollview.zoomScale = 1.0;
          self.imageView.image = frame.image;
          [self.scrollview zoomToRect:frame.zoomToRectInImage animated:YES];
      }
}

Но у меня возникают проблемы с синхронизацией между изменениями изображения и масштабированием.

Как я могу убедиться, что они не перекрываются?

3 ответа

Решение

Если ваш массив кадров является динамическим или массив становится большим, продолжение задачи становится немного громоздким для реализации.

С помощью сторонней библиотеки (ну, только одного класса) это может выглядеть так:

Сначала определите задачу анимации следующим образом:

- (RXPromise*) animationTaskWithFrame(AnimationFrame* frame) 
{
    RXPromise* promise = [RXPromise new];
    [UIView animateWithDuration:0.5 animations:^{
        ...  // animation block 
    }
    completion:^(BOOL finished) {
         [promise fulfillWithValue:@"OK"];
    }];

    return promise;
}

Затем, предполагая, что вы реализовали категорию для NSArray:

typedef RXPromise* ^(unary_async_t)(id param);

@interface NSArray (RXExtension)
- (RXPromise*) forEachApplyTask:(unary_async_t)task;
@end

Вы можете запустить свои кадры следующим образом:

-(void)animateImageView
{
    NSArray* myAnimationFrames = @[frame1,frame2,frame3,frame4,frame5]; 

    // start the animations asynchronously:
    self.myAnimationFramesPromise = [myAnimationFrames forEachApplyTask:^RXPromise*(id frame){
        return [self animationTaskWithFrame:frame];
    }];

    // When finished, then:
    self.myAnimationFramesPromise.then(^id(id result){
        // All animations finished.
        NSLog(@"Result: %@", result);  // prints @"Finished"
        return nil;
    }, nil /* error handler*/);

}

Если вы хотите отменить анимацию до того, как все закончится:

// elsewhere:

- (void) viewWillDisappear:(BOOL)animated {

     [self.myAnimationFramesPromise cancel];
}

Реализация категории показана ниже. Обратите внимание, что это общая реализация, она просто последовательно запускает асинхронную функцию, указанную задачей блока для каждого элемента в массиве по порядку. Итак, это инструмент, который можно использовать и в другом месте - не только для анимации.

static RXPromise* do_apply_each(NSEnumerator* iter, RXPromise* promiseResult, unary_async_t task)
{
    if (promiseResult.isCancelled) {
        return promiseResult;
    }
    id obj = [iter nextObject];
    if (obj == nil) {
        [promiseResult fulfillWithValue:@"Finished"];
        return promiseResult;
    }
    promiseResult = task(obj).then(^id(id result){
        return do_apply_each(iter, promiseResult, task);
    }, ^id(NSError*error){
        return error;
    });
    return promiseResult;
}


@implementation NSArray (RXExtension)

- (RXPromise*) forEachApplyTask:(unary_async_t)task {
    RXPromise* promise = [RXPromise new];
    NSEnumerator* iter = [self objectEnumerator];
    return do_apply_each(iter, promise, task);
}

@end

RXPromise доступен на GitHub: RXPromise. Раскрытие: я автор.

Попробуйте этот код,

[UIView animateWithDuration:0.5
                 animations:^
{ 
//do the changes of the view after the animation

}
completion:^(BOOL finished)
 {
    //anything to be done after animation
 }
 ];

UIView имеет следующие методы, вы можете использовать эти методы для достижения ваших требований.

[UIView animateWithDuration: animations:];
[UIView animateWithDuration: animations: completion:];
[UIView animateWithDuration: delay: options: animations: completion:]
Другие вопросы по тегам