В 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:]