除了以下(我们从表示层获取当前状态,停止动画,从保存的表示层重置当前状态,并启动新动画)之外,还有一个更简单的解决方案。
如果制作基于块的动画,如果您想在 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做到这一点:
如果您还没有,请将 QuartzCore.framework 添加到您的项目中。(在现代版本的 Xcode 中,通常不需要显式执行此操作,因为它会自动链接到项目。)
如果您还没有导入必要的头文件(同样,在当代版本的 Xcode 中不需要):
#import <QuartzCore/QuartzCore.h>
让您的代码停止现有动画:
[self.subview.layer removeAllAnimations];
获取对当前表示层的引用(即此时的视图状态):
CALayer *currentLayer = self.subview.layer.presentationLayer;
根据 中的当前值重置transform
(或或其他) :frame
presentationLayer
self.subview.layer.transform = currentLayer.transform;
transform
现在从那个(或其他)动画frame
到新值:
[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];
}
顺便说一句,虽然对于真正快速的动画通常并不重要,但对于像您这样的慢速动画,您可能希望将反转动画的持续时间设置为与您在当前动画中的进展程度相同(例如,如果您在 0.5 秒内进入一个 3.0 秒的动画,那么当您反转时,您可能不想花 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];
}