如何使用GCD控制动画序列

时间:2014-12-22 11:25:19

标签: ios animation grand-central-dispatch

我有View来显示和隐藏,以便为用户提供一些提示。

show and hide方法看起来像这样:

-(void)show{
    [UIView animateWithDuration:3.0f
                 animations:^{
                     //do something to show self to give hint;
                     self.frame = CGRectMake(0,0,0,0);
                 } completion:nil];
}

-(void)hide{
    [UIView animateWithDuration:3.0f
                 animations:^{
                     //do something to hide self to give hint;
                     self.frame = CGRectMake(centerX,centerY,100,100);
                 } completion:nil];
}

在展示新视图时,我必须致电hide method,然后致电show method。但持续时间延迟3.0f会导致一些错误。我正在使用这样的方法:

dispatch_async(dispatch_get_main_queue(), ^{
    [view hide];
});

dispatch_async(dispatch_get_main_queue(), ^{
    [view show];
});

我在show method之后立即致电hide method。动画无法按照提交到队列的顺序执行。我想要的是show method完成hide method后完成的show method。如何控制这两种方法的顺序。

我认为我无法使用完成处理程序,因为我无法确定调用这两个方法的位置,或者当我调用另一个hide methodshow时是否显示视图。

如果我不清楚,有什么建议吗?我会重复我的问题。


PS:

它不仅仅是闪光灯。当调用下一个hide方法时,我无法确保显示最后一个视图或隐藏最后一个视图的显示时间,也就是说,如果正在显示视图并且show方法具有调用并已完成,然后调用hide方法,结果是正确的。如果正在显示视图,则需要显示另一个提示视图,我将首先调用show,然后调用main_queue,因为show是串行的,但动画块是同步执行的,所以结果是错的。我在寻找GCD中是否有某种锁可以帮助我在最后一个排队块完成后执行一个块,而不是在hideshow方法中进行更改。因为有许多其他调用hide和{{1}}方法的许多不同类型的参数,我需要在我的代码中修复很多地方。

4 个答案:

答案 0 :(得分:1)

如果你想要的是一个动作(隐藏然后显示自己),你应该只做一个动画而不是加入两个动画。

有两种可能的解决方案。

(1)使用动画重复和自动反转(需要在完成回调时重置回原始大小)

-(void) flash {
  CGRect bounds = self.bounds;

  [UIView animateWithDuration:1.0f
                        delay:0.0f
                      options:UIViewAnimationOptionAutoreverse |
                              UIViewAnimationOptionRepeat
                   animations:^{
                     [UIView setAnimationRepeatCount:1];
                     self.bounds = CGRectZero;
                   }
                   completion:^(BOOL finished) {
                     self.bounds = bounds;
                   }];
}

(2)使用关键帧动画

-(void) flash2 {
    [UIView animateKeyframesWithDuration:1.0f
        delay:0.0f
      options:UIViewKeyframeAnimationOptionCalculationModeLinear
   animations:^{
       CGRect bounds = self.bounds;

       [UIView addKeyframeWithRelativeStartTime:0.0
                               relativeDuration:0.5
                                     animations:^{ self.bounds = CGRectZero; }];

       [UIView addKeyframeWithRelativeStartTime:0.5
                               relativeDuration:0.5
                                     animations:^{ self.bounds = bounds; }];
    }
   completion:nil];
}

答案 1 :(得分:1)

如果要按照添加到队列的顺序一次执行一个任务,请使用串行队列。

因此,您可以使用串行队列以添加的顺序一次执行显示和隐藏任务。是的,主要队列是可以的。

然而UIView -animateWithDuration:animations:方法是一种异步调用,该方法立即返回。所以你需要等到调用完成块。

如果您想等到某些任务完成,请使用调度组。但你应该避免在主队列上这样等待。它阻止主队列。糟糕的应用。

因此,您可能需要使用串行队列和调度组作为以下内容。

属性并初始化

@property (nonatomic, strong) dispatch_queue_t serialQueue;
@property (nonatomic, strong) dispatch_group_t group;

-(void)initQueue {
    // create a serial queue
    self.serialQueue = dispatch_queue_create("com.example.serialQueue", 0);
    // create a dispatch group
    self.group = dispatch_group_create();
}

使用串行队列和调度组的方法

-(void)animateSyncWithDuration:(NSTimeInterval)duration animations:(block_t)animations {
    dispatch_async(self.serialQueue, ^{
        /*
         * This block is invoked on the serial queue
         * This block would never be executed concurrently
         */

        /*
         * Enter the dispatch group
         */
        dispatch_group_enter(self.group);

        dispatch_async(dispatch_get_main_queue(), ^{
            /*
             * This block is invoked on the main queue
             * It is safe to use UIKit
             */
            [UIView animateWithDuration:duration animations:animations completion:^{
                /*
                 * This completion block is invoked on the main queue
                 * Now leave the dispatch group
                 */
                dispatch_group_leave(self.group);
            }];
        });

        /*
         * Wait until leaving the dispatch group from the UIView animation completion block
         * It means it blocks the serial queue
         */
        dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);
    });
}

显示和隐藏

-(void)show{
    [self animateSyncWithDuration:3.0f animations:^{
        //do something to show self to give hint;
        self.frame = CGRectMake(0,0,0,0);
    }];
}

-(void)hide{
    [self animateSyncWithDuration:3.0f animations:^{
        //do something to hide self to give hint;
        self.frame = CGRectMake(centerX,centerY,100,100);
    }];
}

答案 2 :(得分:0)

我使用以下方式将delay放入function calling

- (void) doAnimation : (double) delay {

    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"Call your First function here");
    });

    double delayInSeconds = delay;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

        NSLog(@"Call your second function here");

    });
} 

答案 3 :(得分:0)

我可能没有完全理解用例,但我认为你应该在这里做的是检查是否真的需要进行隐藏操作。此外,由于隐藏在代码中的动画持续时间为3秒,因此您应该使用完成块创建方法,这样您就可以执行类似于我在伪代码中编写的内容:

- (void)hideIfNeededWithCompletionBlock:((^)())completionBlock {
    if (self.isShowing) {
        [self hideWithCompletionBlock:^(BOOL didHide) {
            if (completionBlock) {
                completionBlock();
            }
        }];
    } else {
        if (completionBlock) {
            //We didn't need to hide anything, so we're done
            completionBlock();
        }
    }
}

然后你可以这样称呼它:

[self hideIfNeededWithCompletionBlock:^(){
    [self show];
}];

如果您需要灵活性,可以使用show方法执行类似的操作。

此外,根据您的需要,您可以使您的方法采用BOOL来设置是否为显示/隐藏设置动画,如果传递NO,则使用0.0的持续时间。

我认为它可以对抗UIView动画API,当你可以使用提供的API处理它时,开始将它包装在dispatch_async块中。