如何停止和反转UIView动画?

时间:2013-04-30 14:59:56

标签: ios animation uiview uiviewanimation

我为UIView设置了动画,以便在用户触摸切换按钮时缩小,当用户再次触摸按钮时,它会扩展回原始大小。到目前为止一切正常。问题是动画需要一些时间 - 例如3秒。在此期间,我仍然希望用户能够与界面进行交互。因此,当用户在动画仍在进行时再次触摸按钮时,动画应该停在正确的位置并反转。

在Apple Q& As中我找到了一种立即暂停所有动画的方法:

https://developer.apple.com/library/ios/#qa/qa2009/qa1673.html

但我没有看到从这里反转动画的方法(并省略了其余的初始动画)。我该如何做到这一点?

- (IBAction)toggleMeter:(id)sender {
    if (self.myView.hidden) {        
        self.myView.hidden = NO;
        [UIView animateWithDuration:3 animations:^{
            self.myView.transform = expandMatrix;
        } completion:nil];
    } else {
        [UIView animateWithDuration:3 animations:^{
            self.myView.transform = shrinkMatrix;
        } completion:^(BOOL finished) {
            self.myView.hidden = YES;
        }];
    }
}

3 个答案:

答案 0 :(得分:42)

除了以下(我们从表示层获取当前状态,停止动画,从保存的表示层重置当前状态,并启动新动画),还有一个更容易的解决方案。 / p>

如果要执行基于块的动画,如果要停止动画并在8.0之前的iOS版本中启动新动画,则只需使用UIViewAnimationOptionBeginFromCurrentState选项即可。 (在iOS 8中有效,默认行为不仅是从当前状态开始,而是以反映当前位置和当前速度的方式执行此操作,因此根本不必担心此问题有关详细信息,请参阅WWDC 2014视频Building Interruptible and Responsive Interactions。)

[UIView animateWithDuration:3.0
                      delay:0.0
                    options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction
                 animations:^{
                     // specify the new `frame`, `transform`, etc. here
                 }
                 completion:NULL];

您可以通过停止当前动画并从当前动画停止的位置开始新动画来实现此目的。您可以使用Quartz 2D执行此操作:

  1. Add QuartzCore.framework to your project如果您还没有。 (在Xcode的当代版本中,通常不必明确地执行此操作,因为它会自动链接到项目。)

  2. 如果您还没有导入必要的标题(再次,在当代版本的Xcode中不需要):

    #import <QuartzCore/QuartzCore.h>
    
  3. 让您的代码停止现有动画:

    [self.subview.layer removeAllAnimations];
    
  4. 获取对当前表示层的引用(即正好在此时的视图状态):

    CALayer *currentLayer = self.subview.layer.presentationLayer;
    
  5. 根据transform中的当前值重置frame(或presentationLayer或其他):

    self.subview.layer.transform = currentLayer.transform;
    
  6. 现在从transform(或frame或其他)动画到新值:

    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:UIViewAnimationOptionAllowUserInteraction
                     animations:^{
                         self.subview.layer.transform = newTransform;
                     }
                     completion:NULL];
    
  7. 将所有这些放在一起,这是一个例程,可以将我的变换比例从2.0x切换到识别并返回:

    - (IBAction)didTouchUpInsideAnimateButton:(id)sender
    {
        CALayer *currentLayer = self.subview.layer.presentationLayer;
    
        [self.subview.layer removeAllAnimations];
    
        self.subview.layer.transform = currentLayer.transform;
    
        CATransform3D newTransform;
    
        self.large = !self.large;
    
        if (self.large)
            newTransform = CATransform3DMakeScale(2.0, 2.0, 1.0);
        else
            newTransform = CATransform3DIdentity;
    
        [UIView animateWithDuration:1.0
                              delay:0.0
                            options:UIViewAnimationOptionAllowUserInteraction
                         animations:^{
                             self.subview.layer.transform = newTransform;
                         }
                         completion:NULL];
    }
    

    或者,如果您想将frame尺寸从100x100切换到200x200并返回:

    - (IBAction)didTouchUpInsideAnimateButton:(id)sender
    {
        CALayer *currentLayer = self.subview.layer.presentationLayer;
    
        [self.subview.layer removeAllAnimations];
    
        CGRect newFrame = currentLayer.frame;
    
        self.subview.frame = currentLayer.frame;
    
        self.large = !self.large;
    
        if (self.large)
            newFrame.size = CGSizeMake(200.0, 200.0);
        else
            newFrame.size = CGSizeMake(100.0, 100.0);
    
        [UIView animateWithDuration:1.0
                              delay:0.0
                            options:UIViewAnimationOptionAllowUserInteraction
                         animations:^{
                             self.subview.frame = newFrame;
                         }
                         completion:NULL];
    }
    

    顺便说一下,虽然它对于非常快速的动画通常并不重要,但对于像你这样的慢动画,你可能想要将倒车动画的持续时间设置为与你的动画相同程度。在你当前的动画中取得了进展(例如,如果你在3.0秒动画中0.5秒,当你逆转时,你可能不想花3.0秒来扭转你动画的那一小部分动画到目前为止完成,但只是0.5秒)。因此,这可能看起来像:

    - (IBAction)didTouchUpInsideAnimateButton:(id)sender
    {
        CFTimeInterval duration = kAnimationDuration;             // default the duration to some constant
        CFTimeInterval currentMediaTime = CACurrentMediaTime();   // get the current media time
        static CFTimeInterval lastAnimationStart = 0.0;           // media time of last animation (zero the first time)
    
        // if we previously animated, then calculate how far along in the previous animation we were
        // and we'll use that for the duration of the reversing animation; if larger than
        // kAnimationDuration that means the prior animation was done, so we'll just use
        // kAnimationDuration for the length of this animation
    
        if (lastAnimationStart)
            duration = MIN(kAnimationDuration, (currentMediaTime - lastAnimationStart));
    
        // save our media time for future reference (i.e. future invocations of this routine)
    
        lastAnimationStart = currentMediaTime;
    
        // if you want the animations to stay relative the same speed if reversing an ongoing
        // reversal, you can backdate the lastAnimationStart to what the lastAnimationStart
        // would have been if it was a full animation; if you don't do this, if you repeatedly
        // reverse a reversal that is still in progress, they'll incrementally speed up.
    
        if (duration < kAnimationDuration)
            lastAnimationStart -= (kAnimationDuration - duration);
    
        // grab the state of the layer as it is right now
    
        CALayer *currentLayer = self.subview.layer.presentationLayer;
    
        // cancel any animations in progress
    
        [self.subview.layer removeAllAnimations];
    
        // set the transform to be as it is now, possibly in the middle of an animation
    
        self.subview.layer.transform = currentLayer.transform;
    
        // toggle our flag as to whether we're looking at large view or not
    
        self.large = !self.large;
    
        // set the transform based upon the state of the `large` boolean
    
        CATransform3D newTransform;
    
        if (self.large)
            newTransform = CATransform3DMakeScale(2.0, 2.0, 1.0);
        else
            newTransform = CATransform3DIdentity;
    
        // now animate to our new setting
    
        [UIView animateWithDuration:duration
                              delay:0.0
                            options:UIViewAnimationOptionAllowUserInteraction
                         animations:^{
                             self.subview.layer.transform = newTransform;
                         }
                         completion:NULL];
    }
    

答案 1 :(得分:1)

您可以使用一个常见的技巧来完成此操作,但有必要编写一个单独的缩小方法(以及另一个类似的扩展方法):

- (void) shrink {  
    [UIView animateWithDuration:0.3
                     animations:^{
                        self.myView.transform = shrinkALittleBitMatrix;
                     }
                     completion:^(BOOL finished){
                          if (continueShrinking && size>0) {
                              size=size-1;
                              [self shrink];      
                           }
                     }];
}

所以现在,诀窍是打破缩小为10秒动画(当然还有超过10个)的3秒动画,每个动画缩小0.3秒,其中你缩小整个动画的十分之一:shrinkALittleBitMatrix 。每个动画完成后,只有当bool ivar continueShrinking为真且int ivar size为正时才调用相同的方法(全尺寸的视图大小= 10且视图最小)大小将是size = 0)。按下按钮时,将ivar continueShrinking更改为FALSE,然后调用expand。这将在不到0.3秒的时间内停止动画。

嗯,你必须填写详细信息,但我希望它有所帮助。

答案 2 :(得分:0)

首先:如何删除或取消带有视图的动画?

[view.layer removeAllAnimations] 

如果视图有很多动画,例如,一个动画从上到下移动,另一个动画从左到右移动;

你可以取消或删除这样的特殊动画:

[view.layer removeAnimationForKey:@"someKey"];
// the key is you assign when you create a animation 
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"someKey"]; 

当你这样做时,动画将停止,它将调用它的委托:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

如果flag == 1,则表示动画已完成。 如果flag == 0,表示动画未完成,可能会取消,删除。

第二:所以,您可以在此委托方法中执行您想要执行的操作。

如果你想在删除代码执行时获取视图的框架,你可以这样做:

currentFrame = view.layer.presentationlayer.frame;

注意:

  

当您获得当前帧并删除动画时,视图也会设置一段时间的动画,因此currentFrame不是设备屏幕中的最后一帧。

     

我现在无法解决这个问题。如果有一天我可以,我会更新这个问题。