既にアニメーション化されているビューにアニメーションを適用すると、既存のアニメーションが新しいアニメーションに置き換えられます。ただ最後まで突っ込むわけではありません。それが、あなたのバージョンが思い通りに動かない理由です。
これは、新しいアニメーションに遅延を設定した場合でも発生します。既存のアニメーションが削除され、指定された遅延の後に新しいアニメーションが開始されます。これにより、k20 の簡単な回答が機能しなくなります。(それは本当に簡単な解決策になるので、残念です。)
そのため、非常に簡単な代替方法は、各追加アニメーションの追加をまったく延期することです。これは、関数を使用すると非常に簡単dispatch_after
です。まず、ビューの Y 原点をヘルパー関数にスワップするコードを取り出してみましょう。
static void swapViewYOrigins(UIView *a, UIView *b) {
CGRect aFrame = a.frame;
CGRect bFrame = b.frame;
CGFloat t = aFrame.origin.y;
aFrame.origin.y = bFrame.origin.y;
bFrame.origin.y = t;
a.frame = aFrame;
b.frame = bFrame;
}
これで並べ替え関数を記述して、dispatch_after
連続する各アニメーションを延期するために使用できます。
- (IBAction)sortButtonWasTapped {
NSTimeInterval duration = 1;
dispatch_time_t time = DISPATCH_TIME_NOW;
for (int i = imagesArray.count - 1; i > 0; --i) {
for (int j = 0; j < i; ++j) {
UIView *a = imagesArray[j];
UIView *b = imagesArray[j+1];
if (a.frame.size.width > b.frame.size.width) {
imagesArray[j] = b;
imagesArray[j+1] = a;
dispatch_after(time, dispatch_get_main_queue(), ^{
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
swapViewYOrigins(a, b);
} completion:nil];
});
time = dispatch_time(time, duration * NSEC_PER_SEC);
}
}
}
}
したがって、これは問題のない解決策ですが、1 つの欠点があります。dispatch_after
保留中のブロックをキューから削除する API がないため、アニメーションをキャンセルするボタンを追加するのは簡単ではありません。
キャンセルをサポートしたい場合は、保留中の操作のキューを明示的に管理することをお勧めします。キューを保持するインスタンス変数を追加することで、これを行うことができます。
@implementation ViewController {
IBOutletCollection(UIView) NSMutableArray *imagesArray;
NSMutableArray *pendingSwaps;
}
次に、 をsortButtonWasTapped
呼び出す代わりにキューにブロックを追加するように変更し、dispatch_after
並べ替えが終了した後にキューの実行を開始します。
- (IBAction)sortButtonWasTapped {
pendingSwaps = [NSMutableArray array];
for (int i = imagesArray.count - 1; i > 0; --i) {
for (int j = 0; j < i; ++j) {
UIView *a = imagesArray[j];
UIView *b = imagesArray[j+1];
if (a.frame.size.width > b.frame.size.width) {
imagesArray[j] = b;
imagesArray[j+1] = a;
[pendingSwaps addObject:^{
swapViewYOrigins(a, b);
}];
}
}
}
[self runPendingSwaps];
}
以前と同じswapViewYOrigins
機能を使用しています。保留中のスワップを次のように実行します。
- (void)runPendingSwaps {
if (pendingSwaps.count == 0)
return;
void (^swapBlock)(void) = pendingSwaps[0];
[pendingSwaps removeObjectAtIndex:0];
[UIView animateWithDuration:1 animations:swapBlock completion:^(BOOL finished) {
[self runPendingSwaps];
}];
}
したがって、キューが空の場合は停止します。それ以外の場合は、キューから最初のブロックを取り出し、それをアニメーションのアニメーション ブロックとして使用しますUIView
。runPendingSwaps
アニメーションに、現在のアニメーションが終了した後に次の保留中のアニメーション (ある場合) を開始する完了ブロックを再度呼び出します。
pendingSwaps
キャンセルするには、 nilに設定するだけです。
- (IBAction)cancelButtonWasTapped {
pendingSwaps = nil;
}
ユーザーが [キャンセル] をタップした場合、が戻るときに完全に並べ替えられているimagesArray
ため、ビューは必ずしも画面上の順序と同じになるとは限らないことに注意してください。バブル ソートを開始する前に、(組み込みのソートを使用して) Y 原点でソートすることで、これを修正できます。imagesArray
sortButtonWasTapped
imagesArray
- (IBAction)sortButtonWasTapped {
[self sortImagesArrayByY];
// etc. same as previous version
}
- (void)sortImagesArrayByY {
[imagesArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
CGFloat d = [obj1 frame].origin.y - [obj2 frame].origin.y;
return
d < 0 ? NSOrderedAscending
: d > 0 ? NSOrderedDescending
: NSOrderedSame;
}];
}
この状態で、[並べ替え] ボタンをクリックし、ビューが画面上で完全に並べ替えられる前に [キャンセル] をクリックし、もう一度 [並べ替え] をクリックすると、(バブル 並べ替えを再度実行して) 再開されます。