如何停止和反转UIView动画?

Mis*_*cha 15 animation uiview uiviewanimation ios

我已经为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;
        }];
    }
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 42

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

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

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

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

  1. 如果您还没有将QuartzCore.framework添加到项目中.(在Xcode的当代版本中,通常不必显式执行此操作,因为它会自动链接到项目.)

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

    #import <QuartzCore/QuartzCore.h>
    
    Run Code Online (Sandbox Code Playgroud)
  3. 让代码停止现有动画:

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

    CALayer *currentLayer = self.subview.layer.presentationLayer;
    
    Run Code Online (Sandbox Code Playgroud)
  5. 重置transform(或frame或任何)根据在电流值presentationLayer:

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

    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:UIViewAnimationOptionAllowUserInteraction
                     animations:^{
                         self.subview.layer.transform = newTransform;
                     }
                     completion:NULL];
    
    Run Code Online (Sandbox Code Playgroud)

把这一切放在一起,这是一个例程,可以将我的变换比例从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];
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您想将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];
}
Run Code Online (Sandbox Code Playgroud)

顺便说一下,虽然它对于真正快速的动画通常并不重要,但对于像你这样的慢动画,你可能想要将反转动画的持续时间设置为与当前动画中的进度相同(例如,如果你进入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];
}
Run Code Online (Sandbox Code Playgroud)