以下 (プレゼンテーション レイヤーから現在の状態を取得し、アニメーションを停止し、保存されたプレゼンテーション レイヤーから現在の状態をリセットし、新しいアニメーションを開始する) に加えて、はるかに簡単な解決策があります。
ブロックベースのアニメーションを実行している場合、アニメーションを停止して 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];
}
ところで、非常に速いアニメーションの場合は一般的にそれほど重要ではありませんが、あなたのような遅いアニメーションの場合は、反転アニメーションの継続時間を現在のアニメーションでどれだけ進んだかと同じに設定したい場合があります (たとえば、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];
}