フレームの配列が動的であるか、配列が大きくなると、タスクの継続を実装するのが少し扱いにくくなります。
サードパーティのライブラリ (つまり、1 つのクラスのみ) を使用すると、次のようになります。
まず、アニメーション タスクを次のように定義します。
- (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で入手できます。開示:私は著者です。