如何停止和扭转UIViewanimation?

我有一个UIView的animation,以便它在用户触摸一个切换button时收缩,当用户再次触摸button时,它将扩展回原始大小。 到目前为止,一切正常。 问题是animation需要一些时间 – 例如3秒。 在此期间,我仍然希望用户能够与界面进行交互。 所以当用户在animation仍在进行的时候再次触摸button时,animation应该停止在原来的位置并且反向。

在苹果问答中,我发现了一种立即暂停所有animation的方法:

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

但我没有看到从这里倒转animation的方法(并省略了最初的animation的其余部分)。 我如何做到这一点?

- (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; }]; } } 

除了下面(从表示层抓取当前状态,停止animation,从保存的表示层重置当前状态,并启动新的animation),还有一个更简单的解决scheme。

如果要执行基于块的animation,如果要停止animation并在8.0之前的iOS版本中启动新animation,则可以简单地使用UIViewAnimationOptionBeginFromCurrentState选项。 (在iOS 8中有效,默认行为不仅是从当前状态开始,而且是以反映当前位置以及当前速度的方式进行的,从而使得根本不必担心这个问题。查看WWDC 2014videoBuilding Interruptible and Responsive Interactions了解更多信息。)

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

您可以通过停止当前animation并从当前animation停止的位置开始新animation来实现此目的。 你可以用Quartz 2D做到这一点:

  1. 如果你还没有添加QuartzCore.framework到你的项目 。 (在当代版本的Xcode中,通常没有必要明确地这样做,因为它自动链接到项目。)

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

     #import <QuartzCore/QuartzCore.h> 
  3. 让你的代码停止现有的animation:

     [self.subview.layer removeAllAnimations]; 
  4. 获取当前表示层的引用(即视图的状态,因为它恰好在这个时刻):

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

     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]; 

把所有这些放在一起,这是一个例程,从2.0x切换到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尺寸从100×100切换到200×200,并返回:

 - (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]; } 

顺便说一下,虽然对于真正快速的animation通常并不重要,但对于像您这样的慢速animation,您可能需要将倒退animation的持续时间设置为与当前animation中进展的距离相同例如,如果您在3.0秒animation中使用0.5秒,则在倒转时,您可能不想花费3.0秒来逆转到目前为止所完成的animation的一小部分,而只需要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 (ie 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]; } 

有一个常见的技巧可以用来做到这一点,但有必要编写一个单独的方法来收缩(和另一个类似的扩展):

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

所以现在,诀窍是将3秒钟的animation缩小为10个animation(或者当然是10个以上),每个animation缩短0.3秒,其中缩小整个animation的shrinkALittleBitMatrixshrinkALittleBitMatrix 。 每个animation完成后,只有当布尔伊娃continueShrinking size为真,并且当整个伊娃的size为正值(全尺寸的视图尺寸= 10,最小尺寸的视图尺寸= 0)时才调用相同的方法。 当您按下button时,您将伊娃continueShrinking缩小为FALSE,然后调用expand 。 这将在不到0.3秒的时间内停止animation。

那么,你必须填写的细节,但我希望它可以帮助。

首先 :如何删除或取消animation与视图?

 [view.layer removeAllAnimations] 

如果视图有很多animation,比如一个animation是从上到下,另一个是从左到右的,

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

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

当你这样做,animation将停止,它会调用它的委托:

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

如果标志== 1,则表示animation已完成。 如果标志== 0,表示animation未完成,可能取消,删除。

第二:所以,你可以在这个委托方法中做你想做的事情。

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

 currentFrame = view.layer.presentationlayer.frame; 

注意:

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

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