UIViewをアニメーション化して、ユーザーがトグルボタンに触れると縮小し、ユーザーがもう一度ボタンに触れると元のサイズに戻るようにしました。これまでのところ、すべてが正常に機能しています。問題は、アニメーションに時間がかかることです。 3秒。その間も、ユーザーがインターフェイスを操作できるようにしたいと思います。したがって、アニメーションの進行中にユーザーがボタンをもう一度タッチすると、アニメーションはその場所で停止し、逆になります。
Apple Q&Aで、すべてのアニメーションをすぐに一時停止する方法を見つけました。
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;
}];
}
}
以下(プレゼンテーション層から現在の状態を取得し、アニメーションを停止し、保存されたプレゼンテーション層から現在の状態をリセットし、新しいアニメーションを開始する)に加えて、はるかに簡単な解決策があります。
ブロックベースのアニメーションを実行している場合、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];
これを実現するには、現在のアニメーションを停止し、現在のアニメーションを中断したところから新しいアニメーションを開始します。これは 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];
}
これを行うために使用できる一般的なトリックがありますが、縮小する別のメソッド(および拡張する別の同様のメソッド)を作成する必要があります。
- (void) shrink {
[UIView animateWithDuration:0.3
animations:^{
self.myView.transform = shrinkALittleBitMatrix;
}
completion:^(BOOL finished){
if (continueShrinking && size>0) {
size=size-1;
[self shrink];
}
}];
}
だから今、トリックは、縮小する3秒のアニメーションをそれぞれ0.3秒の10のアニメーション(またはもちろん10以上)に分割し、アニメーション全体の1/10に縮小することです:shrinkALittleBitMatrix
。各アニメーションが終了した後、bool ivar continueShrinking
がtrueで、int ivar size
が正の場合にのみ同じメソッドを呼び出します(フルサイズのビューはsize = 10になり、ビューはsize = 10になります)。最小サイズの場合はsize = 0になります)。ボタンを押すと、ivar continueShrinking
をFALSEに変更してから、expand
を呼び出します。これにより、0.3秒未満でアニメーションが停止します。
さて、あなたは詳細を記入しなければなりませんが、私はそれが役立つことを願っています。
最初:ビュー付きのアニメーションを削除またはキャンセルする方法は?
[view.layer removeAllAnimations]
ビューに多くのアニメーションがある場合。たとえば、1つのアニメーションが上から下に移動し、もう1つのアニメーションが左から右に移動します。
次のような特別なアニメーションをキャンセルまたは削除できます。
[view.layer removeAnimationForKey:@"someKey"];
// the key is you assign when you create a animation
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"someKey"];
これを行うと、アニメーションが停止し、そのデリゲートが呼び出されます。
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
フラグ== 1の場合、アニメーションが完了したことを示します。フラグ== 0の場合、アニメーションが完了していないことを示します。キャンセルされたり、削除されたりする可能性があります。
Second:なので、このデリゲートメソッドでやりたいことができます。
削除コードの実行時にビューのフレームを取得する場合は、次のようにします。
currentFrame = view.layer.presentationlayer.frame;
注:
現在のフレームを取得してアニメーションを削除すると、ビューも一定期間アニメーション化されるため、
currentFrame
はデバイス画面の最後のフレームではありません。現在、この質問を解決することはできません。いつかできれば、この質問を更新します。