如果应用动画为已被动画视图,新的动画替换现有的动画。它不只是被固定在最后。这就是为什么你的版本不能按你想要的方式工作。
即使您在新动画上设置了延迟,也会发生这种情况。现有的动画将被删除,并且新的动画将在指定的延迟后开始。这可以防止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);
}
}
}
}
所以这是一个好的解决方案,但它确实有一个缺点。添加一个取消动画的按钮并不容易,因为没有API从队列中删除待处理的dispatch_after
块。
如果您要支持取消,最好明确地管理未决操作的队列。我们可以做的是通过添加一个实例变量来保存队列:
@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
,因为当sortButtonWasTapped
返回时,imagesArray
已完全排序。我们可以解决这个问题通过启动冒泡排序前由Y起源排序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;
}];
}
有了这个地方,你可以点击排序按钮,然后单击取消之前的看法有在屏幕上完全排序,然后再次点击排序,它会恢复(再次执行冒泡排序)。
感谢罗布帮助它结束了完美的工作。 – user1830229
难道不可以使用完成块吗? – k20
我的代码确实使用完成块。 –