16

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;
        }];
    }
}
4

3 に答える 3

44

以下 (プレゼンテーション レイヤーから現在の状態を取得し、アニメーションを停止し、保存されたプレゼンテーション レイヤーから現在の状態をリセットし、新しいアニメーションを開始する) に加えて、はるかに簡単な解決策があります。

ブロックベースのアニメーションを実行している場合、アニメーションを停止して 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でこれを行うことができます:

  1. プロジェクトに QuartzCore.framework をまだ追加していない場合は追加します。(Xcode の現在のバージョンでは、プロジェクトに自動的にリンクされるため、これを明示的に行う必要がないことがよくあります。)

  2. 必要なヘッダーをまだインポートしていない場合はインポートします (Xcode の現在のバージョンでは必要ありません)。

    #import <QuartzCore/QuartzCore.h>
    
  3. コードで既存のアニメーションを停止します。

    [self.subview.layer removeAllAnimations];
    
  4. 現在のプレゼンテーション レイヤーへの参照を取得します (つまり、現時点でのビューの状態)。

    CALayer *currentLayer = self.subview.layer.presentationLayer;
    
  5. の現在の値に従ってtransform(または何でも)をリセットします。framepresentationLayer

    self.subview.layer.transform = currentLayer.transform;
    
  6. 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];
}
于 2013-04-30T16:16:24.490 に答える
1

これを行うために使用できる一般的なトリックがありますが、縮小する別のメソッド (および拡大する別の同様のメソッド) を記述する必要があります。

- (void) shrink {  
    [UIView animateWithDuration:0.3
                     animations:^{
                        self.myView.transform = shrinkALittleBitMatrix;
                     }
                     completion:^(BOOL finished){
                          if (continueShrinking && size>0) {
                              size=size-1;
                              [self shrink];      
                           }
                     }];
}

ここでの秘訣は、3 秒の縮小アニメーションを、アニメーション全体の 1/10 を縮小するそれぞれ 0.3 秒の 10 個 (またはもちろん 10 個以上) のアニメーションに分割することですshrinkALittleBitMatrix。各アニメーションが終了した後、bool ivarcontinueShrinkingが true で、int ivarsizeが正の場合にのみ同じメソッドを呼び出します (フル サイズのビューは size=10 で、最小サイズのビューは size=0 になります)。ボタンを押すと、 ivarcontinueShrinkingが FALSE に変更され、 が呼び出されますexpand。これにより、アニメーションが 0.3 秒以内に停止します。

詳細を記入する必要がありますが、お役に立てば幸いです。

于 2013-04-30T16:13:10.720 に答える
0

最初:ビューでアニメーションを削除またはキャンセルする方法は?

[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 の場合、アニメーションが完了していないことを示します。キャンセル、削除された可能性があります。

2番目:したがって、このデリゲート メソッドでやりたいことを実行できます。

削除コードの実行時にビューのフレームを取得する場合は、次のようにします。

currentFrame = view.layer.presentationlayer.frame;

ノート:

現在のフレームを取得してアニメーションを削除すると、ビューも一定期間アニメーション化されるためcurrentFrame 、デバイス画面の最後のフレームではありません。

私は今、この質問を解決できません。いつかできれば、この質問を更新します。

于 2016-12-27T11:51:01.830 に答える