web-dev-qa-db-ja.com

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

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

すなわち、私はこのようなことをしました(おそらくアニメーション終了アクションも設定しています):

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

// set view properties

[UIView commitAnimations];

しかし、アニメーションが完了し、アニメーション終了イベントを取得する前に、キャンセルします(短く切ります)。これは可能ですか?グーグルで検索すると、答えのない同じ質問をする人が数人見つかります。1人か2人がそれができないと推測します。

238
philsquared

私がやる方法は、エンドポイントに新しいアニメーションを作成することです。非常に短い期間を設定し、+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];
113

つかいます:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];
357
Jim Heising

all特定のビューでのアニメーションを停止する最も簡単な方法即時は、次のとおりです。

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

#import <QuartzCore/QuartzCore.h>

さて、トラックで死んだビューのすべてのアニメーションを停止したい場合は、次のように言います:

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

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

62
matt

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;
                     }
                 }];
}
47
Ville Laurikari

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

40
tomtaylor

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

IOS 10以降では、 IViewPropertyAnimator !でアニメーションをキャンセルできます。

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()

注:突然のキャンセルを望まない場合は、アニメーションを逆にしたり、一時停止した後にアニメーションを変更したりできます!

20
Tiago Almeida

ビュープロパティの代わりに定数を変更して制約をアニメートする場合、他のメソッドは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];
}
9
Nikola Lajic

アニメーションをスムーズに一時停止/停止する場合

self.yourView.layer.speed = 0;

ソース: レイヤーツリーのアニメーションを一時停止する方法

8
Victor M

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

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

  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
NazCodezz
[UIView setAnimationsEnabled:NO];
// your code here
[UIView setAnimationsEnabled:YES];
5
ideawu

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

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

// set view properties
UIView.commitAnimations()
1
lucamegh

私は同じ問題を抱えています; 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
Carl
CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
    self.frame = pLayer.frame;
}];
0
Thunder Rabbit

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

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

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

0
Gorky

答えられたソリューションはどれも私にとってはうまくいきませんでした。この方法で問題を解決しました(正しい方法であるかどうかわかりませんか?)。これは早すぎる呼び出し(以前のアニメーションがまだ終了していない場合)で問題が発生したためです。必要なアニメーションを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()
                }
        })

    }

}
0
sabiland

removeAllAnimations()以外のUIStackViewアニメーションで作業するときは、removeAllAnimations()が予測不可能な状態に設定できるため、いくつかの値を初期値に設定する必要があります。 view1view2を含むstackViewがあり、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
Paul T.