UIViewアニメーションをキャンセルしますか?


244

UIView進行中のアニメーションをキャンセルすることはできますか?または、CAレベルまで下げる必要がありますか?

つまり、私はこのようなことをしました(多分、アニメーションの終了アクションも設定しています):

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

しかし、アニメーションが完了してアニメーション終了イベントを取得する前に、それをキャンセルしたい(短くします)。これは可能ですか?ぐるぐる回ると、何人かが同じ質問に答えずに質問し、1人または2人がそれを実行できないと推測しています。


途中でアニメーションを一時停止し、そこに残しますか?それとも最初の状態に戻りますか?
Chris Lundie、2009

2
それをそのままの場所に残すか、アニメーション途中(実際にはとにかくすぐに別のアニメーションを開始します)、または終点にまっすぐジャンプします。最初の方がより自然だと思いますが、この場合はどちらでもうまくいきます。
philsquared 2009

3
@Chris Hanson:編集に感謝しますが、iPhoneタグを削除する根拠は何ですか。それはココアタッチによって暗示されるかもしれませんが、私はiPhoneにタグフィルターを持っているので、その場合これを逃します。
philsquared 2009

@Boot To The Head(再び):あなたの質問とそれに対する私自身の応答により、私は少し孤立してテストするように促され、最初のアニメーションが完了する前に新しいアニメーションを開始すると、エンドポイントにジャンプします新しいものを始める前に。
philsquared 2009

-これは私の当面のニーズを満たしています(実際のコードに他のいくつかのバグがあることを示唆しています)が、他の人が同じことを求めていることを知っているので、この質問は開いたままにします。
philsquared 2009

回答:


114

私が行う方法は、エンドポイントに新しいアニメーションを作成することです。非常に短い期間を設定し、+setAnimationBeginsFromCurrentState:メソッドを使用して現在の状態から開始するようにしてください。YESに設定すると、現在のアニメーションが短く表示されます。次のようになります。

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

スティーブン、ありがとう。実はあなたは私の質問の下のコメントを読めば、これは私が最後にやったまさにです-しかし、あなたはここでそれの良い、明確な、アカウントを与えてくれたので、受け入れられたように私はそれをマーキングしています
philsquared

1
@Vojtoどのように?ドキュメントによると、setAnimationBeginsFromCurrentState:iOS 4 の使用は推奨されていませんが、それはまだ存在し、機能するはずです。
スティーブンダーリントン

55
iOS4を+で、animateWithDuration上UIViewAnimationOptionBeginFromCurrentStateオプションを使用します。遅延:オプション:アニメーション:完了:
アダム・エーベルバッハ

UIViewAnimationOptionBeginFromCurrentStateは、進行中のアニメーション、または同じプロパティをターゲットとするアニメーションのみをキャンセルしますか?それがアニメーションである場合、進行中の同じアニメーションを停止せずに、同じポイントで新しいアニメーションを継続させるにはどうすればよいですか?
zakdances 2012

1
@yourfriendzakは、簡単なテストに基づいて、同じプロパティをターゲットとするアニメーションのみをキャンセルするように見えます。contentOffsetがアニメーション化され、contentOffsetアニメーションが継続するscrollViewのアルファを変更してみました。
arlomedia 2012

360

使用する:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];

22
#import <QuartzCore / QuartzCore.h>を追加してコンパイラ警告を削除する必要があることがわかりました;)
deanWombourne

4
とてもシンプルだが、それでも見つけるのは難しい。これをありがとう、私はこれを理解するために1時間過ごしました。
MikeyWard 2010

4
このソリューションで起こりうる問題は、(少なくともiOS 5では)有効になるまでに遅延があることです。つまり、「今すぐアニメーションを停止し、次の行に進む前にアニメーションを停止する」という場合、すぐには機能しません。コード"。
マット

14
+(void)animateWithDuration:(NSTimeInterval)duration animations:(void(^)(void))animations completion:(void(^)(BOOL finished)を使用している場合、これを呼び出すと完了:^(BOOL finished)ブロックがトリガーされることがわかりました)完了
JWGS 2012年

1
@mattアニメーションをすぐに停止するソリューションを知っていますか?アニメーションが実際に停止する前に、少し遅れを観察しています。
tiguero 2013年

62

特定のビューのすべてのアニメーションをすぐに停止する最も簡単な方法は次のとおりです。

プロジェクトをQuartzCore.frameworkにリンクします。コードの始めに:

#import <QuartzCore/QuartzCore.h>

ここで、トラック内のデッドビューのすべてのアニメーションを停止する場合は、次のように言います。

[CATransaction begin];
[theView.layer removeAllAnimations];
[CATransaction commit];

真ん中の行はそれ自体ですべて機能しますが、ランループが終了するまで遅延があります(「再描画の瞬間」)。この遅延を防ぐには、次のように明示的なトランザクションブロックでコマンドをラップします。現在の実行ループのこのレイヤーで他の変更が行われていない場合、これは機能します。


2
私はあなたのコードの後に​​[CATransaction flush]を使用してきました。ただし、0.1秒のパフォーマンスの問題があります。たとえば、特定の時間だけ遅れます。回避策はありますか?
Sidwyn Koh

5
removeAllAnimationsアニメーションを現在の場所で停止するのではなく、アニメーションを最終フレームに持ってくるという副作用があります。
イリヤ2015年

3
通常、アニメーションを停止したい場合、最初のフレームや最後のフレームではなく、現在のフレームで停止することを期待します。最初または最後のフレームにジャンプすると、ぎくしゃくした動きのように見えます。
イリヤ2015年

1
その答えを詳しく説明し、デフォルトの動作とは何か、それをどのように変更できるかを指定します。明らかに、アニメーションをしている人は、彼のメソッド呼び出しの後の画面で何が起こるかに積極的に興味を持っています:)
Ilya

1
詳細は別の場所で説明しました。それは尋ねられたものではないので、私がここで答えたものではありません。別の答えがある場合は、間違いなく答えとしてください!
マット

48

iOS 4以降では、2番目のアニメーションのオプション使用してUIViewAnimationOptionBeginFromCurrentState、最初のアニメーションを短くします。

例として、アクティビティインジケータ付きのビューがあるとします。時間がかかる可能性のあるアクティビティの開始中にアクティビティインジケータをフェードインし、アクティビティが終了したときにフェードアウトしたいとします。以下のコードでは、アクティビティインジケーターが表示されているビューが呼び出されactivityViewます。

- (void)showActivityIndicator {
    activityView.alpha = 0.0;
    activityView.hidden = NO;
    [UIView animateWithDuration:0.5
                 animations:^(void) {
                     activityView.alpha = 1.0;
                 }];

- (void)hideActivityIndicator {
    [UIView animateWithDuration:0.5
                 delay:0 options:UIViewAnimationOptionBeginFromCurrentState
                 animations:^(void) {
                     activityView.alpha = 0.0;
                 }
                 completion:^(BOOL completed) {
                     if (completed) {
                         activityView.hidden = YES;
                     }
                 }];
}

41

アニメーションをキャンセルするには、UIViewアニメーションの外で、現在アニメーション化されているプロパティを設定するだけです。これにより、アニメーションはどこにでも停止し、UIViewは定義した設定にジャンプします。


9
これは部分的にのみ当てはまります。アニメーションは停止しますが、デリゲートメソッド(特に、animationCompleteハンドラー)の起動は停止しません。そのため、そこにロジックがある場合、問題が発生します。
M.ライアン

33
それが「完成した」議論の-animationDidStop:finished:context:目的です。もしそうなら[finished boolValue] == NO、あなたはあなたのアニメーションがどういうわけかキャンセルされたことを知っています。
Nick Forge

これは7年前のすばらしいヒントです。奇妙な振る舞いがあることに注意してください:あなたがアルファを前後にアニメートしているとしましょう。アニメーションを停止するには、アルファをゼロに設定することはできません。あなたはそれを1と言うように設定しました
Fattie '18 / 4/17

26

この回答を復活させて申し訳ありませんが、iOS 10では多少の変更があり、キャンセルが可能になり、正常にキャンセルすることもできます!

iOS 10以降は、UIViewPropertyAnimatorを使用してアニメーションをキャンセルできます。

UIViewPropertyAnimator(duration: 2, dampingRatio: 0.4, animations: {
    view.backgroundColor = .blue
})
animator.stopAnimation(true)

trueを渡すと、アニメーションがキャンセルされ、キャンセルした場所で停止します。完了メソッドは呼び出されません。ただし、falseを渡すと、アニメーションを終了する必要があります。

animator.finishAnimation(.start)

アニメーションを終了して、現在の状態(.current)に留まるか、初期状態(.start)または終了状態(.end)に進むことができます

ちなみに、後で一時停止して再起動することもできます...

animator.pauseAnimation()
animator.startAnimation()

注:突然キャンセルしたくない場合は、アニメーションを元に戻したり、一時停止した後でアニメーションを変更したりすることもできます。


2
これは間違いなく最新のアニメーションに最も関連しています。
Doug

10

ビュープロパティの代わりに定数を変更して制約をアニメーション化する場合、iOS 8では他のどのメソッドも機能しません。

アニメーションの例:

self.constraint.constant = 0;
[self.view updateConstraintsIfNeeded];
[self.view layoutIfNeeded];
[UIView animateWithDuration:1.0f
                      delay:0.0f
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     self.constraint.constant = 1.0f;
                     [self.view layoutIfNeeded];
                 } completion:^(BOOL finished) {

                 }];

解決:

制約の変更の影響を受けるビューのレイヤーとそのサブレイヤーからアニメーションを削除する必要があります。

[self.constraintView.layer removeAllAnimations];
for (CALayer *l in self.constraintView.layer.sublayers)
{
    [l removeAllAnimations];
}

1
またのみself.constraintView.layer.removeAllAnimations()動作します(Swift)
Lachtan

他のソリューションはどれも機能しませんでした。ありがとう、それは私にとっては機能しました。
swapnilagarwal


5

上記のどれも私のためにそれを解決しませんでしたが、これは助けになりました:UIViewアニメーションはプロパティをすぐに設定し、次にアニメーション化します。プレゼンテーションレイヤーがモデル(setプロパティ)と一致すると、アニメーションが停止します。

「あなたが現れるように見えるところからアニメーションしたい」という問題を解決しました(「あなた」はビューを意味します)。あなたがそれを望むなら、それから:

  1. QuartzCoreを追加します。
  2. CALayer * pLayer = theView.layer.presentationLayer;

プレゼンテーションレイヤーに位置を設定する

私はいくつかのオプションを使用します UIViewAnimationOptionOverrideInheritedDuration

しかし、Appleのドキュメントはあいまいなので、使用時に他のアニメーションを本当にオーバーライドするのか、タイマーをリセットするだけなのかわかりません。

[UIView animateWithDuration:blah... 
                    options: UIViewAnimationOptionBeginFromCurrentState ... 
                    animations: ^ {
                                   theView.center = CGPointMake( pLayer.position.x + YOUR_ANIMATION_OFFSET, pLayer.position.y + ANOTHER_ANIMATION_OFFSET);
                   //this only works for translating, but you get the idea if you wanna flip and scale it. 
                   } completion: ^(BOOL complete) {}];

そして、それは今のところまともな解決策になるはずです。


5
[UIView setAnimationsEnabled:NO];
// your code here
[UIView setAnimationsEnabled:YES];

2

スティーブンダーリントンのソリューションのSwiftバージョン

UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.1)
// other animation properties

// set view properties
UIView.commitAnimations()

1

私は同じ問題を抱えています; APIには、特定のアニメーションをキャンセルするものはありません。の

+ (void)setAnimationsEnabled:(BOOL)enabled

すべてのアニメーションを無効にするため、私にとっては機能しません。2つの解決策があります。

1)アニメーションオブジェクトをサブビューにします。次に、そのビューのアニメーションをキャンセルする場合は、ビューを削除するか非表示にします。非常にシンプルですが、表示したままにする必要がある場合は、アニメーションなしでサブビューを再作成する必要があります。

2)次のように、アニメーションを1つだけ繰り返し、必要に応じてデリゲートセレクターを作成してアニメーションを再起動します。

-(void) startAnimation {
NSLog(@"startAnim alpha:%f", self.alpha);
[self setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(pulseAnimationDidStop:finished:context:)];
[self setAlpha:0.1];
[UIView commitAnimations];
}

- (void)pulseAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if(hasFocus) {
    [self startAnimation];
} else {
    self.alpha = 1.0;
}
}

-(void) setHasFocus:(BOOL)_hasFocus {
hasFocus = _hasFocus;
if(hasFocus) {
    [self startAnimation];
}
}

2)の問題は、現在のアニメーションサイクルが終了するときに、アニメーションの停止が常に遅延することです。

お役に立てれば。


1

上記の方法でアニメーションをキャンセルしても、アニメーションはdidStopSelector引き続き実行されます。そのため、アニメーションによって駆動されるアプリケーションにロジック状態がある場合、問題が発生します。このため、上記の方法ではUIViewアニメーションのコンテキスト変数を使用しています。コンテキストパラメータによってプログラムの現在の状態をアニメーションに渡す場合、アニメーションが停止すると、didStopSelector関数は、現在の状態とコンテキストとして渡された状態値に基づいて、何かを実行するか、単に戻るかを決定します。


NickForgeは、これをTomTaylorの上記の正解の下のコメントで完全に説明しています...
Fattie

このメモをどうもありがとう!確かに私は、animationDidStopが呼び出されたときに次のアニメーションが起動されるような状況にあります。単にアニメーションを削除して、すぐに同じキーに新しいアニメーションを再度追加すると、機能しません。たとえば、animationDidStopが別のアニメーションまたはその他の何かをトリガーするまで待機する必要があります。削除されたばかりのアニメーションは、animationDidStopを起動しなくなります。
RickJansen

0
CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
    self.frame = pLayer.frame;
}];

0

状態を元の状態または最終状態に戻さずにアニメーションを一時停止するには:

CFTimeInterval paused_time = [myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
myView.layer.speed = 0.0;
myView.layer.timeOffset = paused_time;

0

UIStackViewアニメーションを操作するときは、removeAllAnimations()いくつかの値を初期値に設定する必要がありますremoveAllAnimations()。予測できない状態に設定する可能性があるためです。私にはstackViewwith view1view2insideがあり、1つのビューが表示され、もう1つが非表示になります:

public func configureStackView(hideView1: Bool, hideView2: Bool) {
    let oldHideView1 = view1.isHidden
    let oldHideView2 = view2.isHidden
    view1.layer.removeAllAnimations()
    view2.layer.removeAllAnimations()
    view.layer.removeAllAnimations()
    stackView.layer.removeAllAnimations()
    // after stopping animation the values are unpredictable, so set values to old
    view1.isHidden = oldHideView1 //    <- Solution is here
    view2.isHidden = oldHideView2 //    <- Solution is here

    UIView.animate(withDuration: 0.3,
                   delay: 0.0,
                   usingSpringWithDamping: 0.9,
                   initialSpringVelocity: 1,
                   options: [],
                   animations: {
                    view1.isHidden = hideView1
                    view2.isHidden = hideView2
                    stackView.layoutIfNeeded()
    },
                   completion: nil)
}

0

回答された解決策のどれも私にとってうまくいきませんでした。この方法で問題を解決しました(正しい方法かどうかわかりません)。これを高速で呼び出すと問題が発生したためです(前のアニメーションがまだ終了していない場合)。希望するアニメーションをcustomAnimブロックで渡します。

extension UIView
{

    func niceCustomTranstion(
        duration: CGFloat = 0.3,
        options: UIView.AnimationOptions = .transitionCrossDissolve,
        customAnim: @escaping () -> Void
        )
    {
        UIView.transition(
            with: self,
            duration: TimeInterval(duration),
            options: options,
            animations: {
                customAnim()
        },
            completion: { (finished) in
                if !finished
                {
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    self.layer.removeAllAnimations()
                    customAnim()
                }
        })

    }

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.