iOS 7 以降では、通常、キーフレーム アニメーションを使用してこの効果を実現します。
たとえば、それぞれがアニメーション全体の 25% を占める 4 つの個別のアニメーションで構成される 2 秒間のアニメーション シーケンスは、次のようになります。
[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionRepeat animations:^{
[UIView addKeyframeWithRelativeStartTime:0.00 relativeDuration:0.25 animations:^{
viewToAnimate.frame = ...;
}];
[UIView addKeyframeWithRelativeStartTime:0.25 relativeDuration:0.25 animations:^{
viewToAnimate.frame = ...;
}];
[UIView addKeyframeWithRelativeStartTime:0.50 relativeDuration:0.25 animations:^{
viewToAnimate.frame = ...;
}];
[UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{
viewToAnimate.frame = ...;
}];
} completion:nil];
以前の iOS バージョンでは、いくつかの方法で一連のアニメーションをキューに入れることができましたが、メイン スレッドでセマフォを使用しないことをお勧めします。
1 つのアプローチは、アニメーションを並行サブクラスでラップすることNSOperation
です。これは、アニメーションが完了するまで完了しません。その後、アニメーションを独自のカスタム シリアル キューに追加できます。
NSOperationQueue *animationQueue = [[NSOperationQueue alloc] init];
animationQueue.maxConcurrentOperationCount = 1;
[animationQueue addOperation:[[AnimationOperation alloc] initWithDuration:1.0 delay:0.0 options:0 animations:^{
viewToAnimate.center = point1;
}]];
[animationQueue addOperation:[[AnimationOperation alloc] initWithDuration:1.0 delay:0.0 options:0 animations:^{
viewToAnimate.center = point2;
}]];
[animationQueue addOperation:[[AnimationOperation alloc] initWithDuration:1.0 delay:0.0 options:0 animations:^{
viewToAnimate.center = point3;
}]];
[animationQueue addOperation:[[AnimationOperation alloc] initWithDuration:1.0 delay:0.0 options:0 animations:^{
viewToAnimate.center = point4;
}]];
サブクラスは次のAnimationOperation
ようになります。
// AnimationOperation.h
#import <Foundation/Foundation.h>
@interface AnimationOperation : NSOperation
- (instancetype)initWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations;
@end
と
// AnimationOperation.m
#import "AnimationOperation.h"
@interface AnimationOperation ()
@property (nonatomic, readwrite, getter = isFinished) BOOL finished;
@property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
@property (nonatomic, copy) void (^animations)(void);
@property (nonatomic) UIViewAnimationOptions options;
@property (nonatomic) NSTimeInterval duration;
@property (nonatomic) NSTimeInterval delay;
@end
@implementation AnimationOperation
@synthesize finished = _finished;
@synthesize executing = _executing;
- (instancetype)initWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations {
self = [super init];
if (self) {
_animations = animations;
_options = options;
_delay = delay;
_duration = duration;
}
return self;
}
- (void)start {
if ([self isCancelled]) {
self.finished = YES;
return;
}
self.executing = YES;
[self main];
}
- (void)main {
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:self.duration delay:self.delay options:self.options animations:self.animations completion:^(BOOL finished) {
[self completeOperation];
}];
});
}
#pragma mark - NSOperation methods
- (void)completeOperation {
if (self.isExecuting) self.executing = NO;
if (!self.isFinished) self.finished = YES;
}
- (BOOL)isAsynchronous {
return YES;
}
- (BOOL)isExecuting {
@synchronized(self) { return _executing; }
}
- (BOOL)isFinished {
@synchronized(self) { return _finished; }
}
- (void)setExecuting:(BOOL)executing {
if (_executing != executing) {
[self willChangeValueForKey:@"isExecuting"];
@synchronized(self) { _executing = executing; }
[self didChangeValueForKey:@"isExecuting"];
}
}
- (void)setFinished:(BOOL)finished {
if (_finished != finished) {
[self willChangeValueForKey:@"isFinished"];
@synchronized(self) { _finished = finished; }
[self didChangeValueForKey:@"isFinished"];
}
}
@end
上記のデモンストレーションでは、シリアル キューを使用しました。ただし、同時キューを使用することもできますが、NSOperation
依存関係を使用してさまざまなアニメーション操作間の関係を管理します。ここにはたくさんのオプションがあります。
アニメーションをキャンセルしたい場合は、次のようにします。
CALayer *layer = [viewToAnimate.layer presentationLayer];
CGRect frame = layer.frame; // identify where it is now
[animationQueue cancelAllOperations]; // cancel the operations
[viewToAnimate.layer removeAllAnimations]; // cancel the animations
viewToAnimate.frame = frame; // set the final position to where it currently is
cancel
必要に応じて、これを操作のカスタム メソッドに組み込むこともできます。