除了下面的方法(我们从展示层获取当前状态,停止动画,从保存的展示层重置当前状态,并启动新动画),还有一个更简单的解决方案。
如果在iOS 8.0之前的版本中进行基于块的动画,如果您想停止动画并启动新动画,您可以简单地使用
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可以做到这一点:
Add QuartzCore.framework to your project if you haven't already. (In contemporary versions of Xcode, it is often unnecessary to explicitly do this as it is automatically linked to the project.)
Import the necessary header if you haven't already (again, not needed in contemporary versions of Xcode):
#import <QuartzCore/QuartzCore.h>
Have your code stop the existing animation:
[self.subview.layer removeAllAnimations]
Get a reference to the current presentation layer (i.e. the state of the view as it is precisely at this moment):
CALayer *currentLayer = self.subview.layer.presentationLayer
Reset the transform
(or frame
or whatever) according to the current value in the presentationLayer
:
self.subview.layer.transform = currentLayer.transform
Now animate from that transform
(or frame
or whatever) to the new value:
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.subview.layer.transform = newTransform;
}
completion:NULL];
将所有内容结合起来,这是一个可以将我的变换比例从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;
CFTimeInterval currentMediaTime = CACurrentMediaTime();
static CFTimeInterval lastAnimationStart = 0.0;
if (lastAnimationStart)
duration = MIN(kAnimationDuration, (currentMediaTime - lastAnimationStart));
lastAnimationStart = currentMediaTime;
if (duration < kAnimationDuration)
lastAnimationStart -= (kAnimationDuration - duration);
CALayer *currentLayer = self.subview.layer.presentationLayer;
[self.subview.layer removeAllAnimations];
self.subview.layer.transform = currentLayer.transform;
self.large = !self.large;
CATransform3D newTransform;
if (self.large)
newTransform = CATransform3DMakeScale(2.0, 2.0, 1.0);
else
newTransform = CATransform3DIdentity;
[UIView animateWithDuration:duration
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.subview.layer.transform = newTransform;
}
completion:NULL];
}
UIViewAnimationOptionCurveLinear
完全精确地工作。如果我使用UIViewAnimationOptionCurveEaseInOut
,反向动画将不会与原始动画完全对称,因为它会缓慢开始并加速,而原始动画会突然停止。是否有一种方法可以以某种方式记住动画的状态,例如“速度”和“加速度”,即获取动画曲线中的当前点? - MischaEaseOut
(即假设当时您正在快速移动),否则进行EaseInOut
。 - RobEaseInOut
(这意味着你正在寻找平滑的动画效果),那么说你要突然反转已经完成了50%的动画(因此以最快的速度移动),并立即将其转向并以不太明显的方式以最快的速度朝另一个方向移动,那么,哇,这将与原始的EaseInOut
意图背道而驰,不是吗? - Robcompletion
块并取消了动画,那么completion
块仍然会执行,如果你有连续的动画,下一个动画可能会触发,所以我们必须利用completion
块中的finished
布尔值来区分成功完成和失败或停止。 - serge-k