web-dev-qa-db-ja.com

NSLayoutConstraintsはアニメート可能ですか?

風景の中の巨大なキーボードでブロックされるように、いくつかのビューをアニメーション化しようとしています。フレームを単純にアニメーション化すればうまくいきますが、他の人はこれが逆効果であると示唆しており、代わりにNSLayoutConstraintsを更新する必要があります。ただし、それらはアニメート可能ではないようです。誰かが彼らを成功に導いてくれましたか?

//heightFromTop is an NSLayoutConstraint referenced from IB
[UIView animateWithDuration:0.25 animations:^{
    self.heightFromTop.constant= 550.f;
}];

その結果、問題の高さに瞬時にジャンプします。

194
borrrden

次の正確なパターンに従ってください。

self.heightFromTop.constant = 550.0f;
[myView setNeedsUpdateConstraints];

[UIView animateWithDuration:0.25f animations:^{
   [myView layoutIfNeeded];
}];

ここで、myViewは、self.heightFromTopが追加されたビューです。アニメーションブロックで行ったのは制約を設定することだけで、レイアウトはすぐには発生しないため、ビューは「ジャンプ」しています。コードでは、レイアウトはheightFromTop.constantを設定した後の次の実行ループで発生し、その時までにアニメーションブロックの範囲外に既にあります。

Swift 2:

self.heightFromTop.constant = 550
myView.setNeedsUpdateConstraints()

UIView.animateWithDuration(0.25, animations: {
   myView.layoutIfNeeded()
})
470
John Estropia

Appleの提案する方法は少し異なります( 「自動レイアウトによる変更のアニメーション化」セクションの例を参照 )。最初に、アニメーションの前にlayoutIfNeededを呼び出す必要があります。次に、アニメーションブロック内にアニメーションを追加し、最後に再度layoutIfNeededを呼び出します。自動レイアウトに移行している私のような人にとっては、アニメーションブロック内のフレームで行っていた以前のアニメーションに似た方法です。 layoutIfNeededを2回呼び出すだけです-アニメーションの前とアニメーションの後に:

[self.view layoutIfNeeded]; // Ensures that all pending layout operations have been completed

[UIView animateWithDuration:1.0f animations:^{

  // Make all constraint changes here
  self.heightFromTop.constant= 550.f;

  [self.view layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes

}];
80
Centurion

@Centurionのアプローチを試しましたが、ストーリーボードから読み込まれた場合、どういうわけか私のビューは間違ったフレームにアニメーション化されます。最初のlayoutIfNeededupdateConstraintsIfNeededに置き換えると、問題はなくなりますが、理由はわかりません。誰もが説明をすることができれば、それは大歓迎です。

[self.view updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0 animations:^{
    self.myConstraint.constant= 100;
    [self.view layoutIfNeeded];
}];
10
Joseph Lin

私は同様の問題を抱えていましたが、このスレッドはそれを乗り越えるのに非常に役立ちました。

エルレイノンからの答えは私を正しい道に導きましたが、少し違う答えを提案したいと思います。アニメートされたトランジションの代わりにジャンプがまだあったので、エルレイノンからの推奨コードは私には機能しませんでした。 cnotethegr8が提供するリンクは、実際の答えを提供してくれました。

自動レイアウトガイドhttps://developer.Apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample .html (ページの一番下まで)。

エルライノンによる回答とのいくつかの違い:

  1. アニメーションメソッドの呼び出しの前に(およびmyViewのsetNeedsUpdateConstraintsの代わりに)コンテナビューでlayoutIfNeededを呼び出します。
  2. アニメーションブロックに新しい制約を設定します。
  3. MyViewの代わりに、アニメーションメソッドのコンテナビューで(制約を設定した後)layoutIfNeededを呼び出します。

これは、上記のリンクのAppleによって提案されたパターンに従います。

特定のビューをアニメーション化し、ボタンをクリックするだけで閉じたり広げたりしたかったのです。私は自動レイアウトを使用しており、コードに寸法(この場合は高さ)をハードコードしたくなかったため、viewDidLayoutSubviewsで高さをキャプチャすることにしました。 autolayoutを使用する場合は、viewWillAppearではなくこのメソッドを使用する必要があります。 viewDidLayoutSubviewsは何度も呼び出される可能性があるため、BOOLを使用して、初期化の最初の実行について通知しました。

// Code snippets

@property (weak, nonatomic) IBOutlet UIView *topView; // Container for minimalView
@property (weak, nonatomic) IBOutlet UIView *minimalView; // View to animate

@property (nonatomic) CGFloat minimalViewFullHeight; // Original height of minimalView

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *minimalViewHeightConstraint;

@property (nonatomic) BOOL executedViewDidLayoutSubviews;


- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];


    // First execution of viewDidLayoutSubviews?
    if(!self.executedViewDidLayoutSubviews){
        self.executedViewDidLayoutSubviews = YES;


        // Record some original dimensions
        self.minimalViewFullHeight = self.minimalView.bounds.size.height;


        // Setup our initial view configuration & let system know that 
        // constraints need to be updated.
        self.minimalViewHeightConstraint.constant = 0.0;
        [self.minimalView setNeedsUpdateConstraints];

        [self.topView layoutIfNeeded];
    }
}

フルアクションスニペ​​ットのサイズ変更

// An action to close our minimal view and show our normal (full) view
- (IBAction)resizeFullAction:(UIButton *)sender {

    [self.topView layoutIfNeeded];

    [UIView transitionWithView:self.minimalView
                  duration:1.0
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{
                    self.minimalViewHeightConstraint.constant = 0.0;

                    // Following call to setNeedsUpdateConstraints may not be necessary
                    [self.minimalView setNeedsUpdateConstraints];

                    [self.topView layoutIfNeeded];

                } completion:^(BOOL finished) {
                    ;
                }];

    // Other code to show full view
    // ...
}

小さなアクションスニペ​​ットのサイズを変更する

// An action to open our minimal view and hide our normal (full) view
- (IBAction)resizeSmallAction:(UIButton *)sender {

    [self.topView layoutIfNeeded];

    [UIView transitionWithView:self.minimalView
                  duration:1.0
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{
                    self.minimalViewHeightConstraint.constant = self.minimalViewFullHeight;
                    [self.minimalView setNeedsUpdateConstraints];

                    [self.topView layoutIfNeeded];

                } completion:^(BOOL finished) {
                    ;
                }];

        // Other code to hide full view
        // ...
    }

必要に応じて、transitionWithViewの代わりにanimateWithDurationを使用できます。

お役に立てれば。

4
Scott Carter