ios 制約の変更をどのようにアニメーション化するのですか?





6 Answers

私は提供された答えを感謝しますが、私はそれをもう少し取ることはいいと思う。

ドキュメンテーションの基本ブロックアニメーション

[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
     // Make all constraint changes here
     [containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];

実際にはこれは非常に単純なシナリオです。 updateConstraintsメソッドを使用してサブビュー制約をアニメーション化する場合はどうすればよいですか?

サブビューを呼び出すアニメーションブロックupdateConstraintsメソッド

[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
    [self.view layoutIfNeeded];
} completion:nil];

updateConstraintsメソッドはUIViewサブクラスでオーバーライドされ、メソッドの最後にsuperを呼び出す必要があります。

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

AutoLayout Guideは多くのことを望みますが、読む価値があります。 私自身はこれを、 UISwitch一部として使用しています。これは、 UITextFieldのペアを使用してサブビューを切り替え、シンプルで繊細な崩壊アニメーション(0.2秒)です。 サブビューの制約は、上記のUIViewサブクラスのupdateConstraintsメソッドで処理されています。

ios animation ios6 autolayout

私はAdBannerView古いアプリを更新しています。広告がない場合は、画面から外れます。 広告があるとき、画面上をスライドします。 基本的なもの。

古いスタイル、私はアニメーションブロックでフレームを設定します。 新しいスタイルでは、Y位置を決定する制約にIBOutletがあります。この場合、それはスーパービューの底からの距離であり、定数を変更します。

- (void)moveBannerOffScreen {
    [UIView animateWithDuration:5
             animations:^{
                          _addBannerDistanceFromBottomConstraint.constant = -32;
                     }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    [UIView animateWithDuration:5
             animations:^{
                         _addBannerDistanceFromBottomConstraint.constant = 0;
             }];
    bannerIsVisible = TRUE;
}

そして、バナーは予想どおりに動きますが、アニメーションはありません。

更新:アニメーションをカバーするWWDC12のビデオ「 マスターレイアウト自動レイアウトのベストプラクティス 」を再見ました。 CoreAnimationを使用して制約を更新する方法について説明します。

私は次のコードで試したが、まったく同じ結果を得る。

- (void)moveBannerOffScreen {
    _addBannerDistanceFromBottomConstraint.constant = -32;
    [UIView animateWithDuration:2
                     animations:^{
                         [self.view setNeedsLayout];
                     }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    _addBannerDistanceFromBottomConstraint.constant = 0;
    [UIView animateWithDuration:2
                     animations:^{
                         [self.view setNeedsLayout];
                     }];
    bannerIsVisible = TRUE;
}

私は何度もチェックをしており、これはメインスレッドで実行されています。




// Step 1, update your constraint
self.myOutletToConstraint.constant = 50; // New height (for example)

// Step 2, trigger animation
[UIView animateWithDuration:2.0 animations:^{

    // Step 3, call layoutIfNeeded on your animated view's parent
    [self.view layoutIfNeeded];
}];



スウィフト4ソリューション

UIView.animate

3つの簡単なステップ:

  1. 制約を変更します。例:

    heightAnchor.constant = 50
    
  2. そのレイアウトが汚れていること、およびautolayoutがレイアウトを再計算する必要があることを含むviewを教えてください:

    self.view.setNeedsLayout()
    
  3. アニメーションブロックでは、レイアウトを再計算するようレイアウトに指示します。これは、フレームを直接設定することに相当します(この場合、autolayoutはフレームを設定します)。

    UIView.animate(withDuration: 0.5) {
        self.view.layoutIfNeeded()
    }
    

完全な単純な例:

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

サイドノート

オプションの0番目のステップ - 制約を変更する前にself.view.layoutIfNeeded()を呼び出すと、アニメーションの開始点が古い制約が適用された状態からのものであることを確認できます(他の制約の変更があった場合アニメーションに含めないでください):

otherConstraint.constant = 30
// this will make sure that otherConstraint won't be animated but will take effect immediately
self.view.layoutIfNeeded()

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

UIViewPropertyAnimator

iOS 10ではUIViewPropertyAnimatorという新しいアニメーションメカニズムがUIViewPropertyAnimatorされているので、基本的に同じメカニズムが適用されることを知っておく必要があります。 手順は基本的に同じです:

heightAnchor.constant = 50
self.view.setNeedsLayout()
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}
animator.startAnimation()

animatorはアニメーションをカプセル化したものなので、それを参照して後で呼び出すことができます。 しかし、アニメーションブロックでは、フレームを再計算するようにautolayoutに指示するだけなので、 startAnimation呼び出す前に制約を変更する必要があります。 したがって、次のようなことが可能です:

// prepare the animator first and keep a reference to it
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}

// at some other point in time we change the constraints and call the animator
heightAnchor.constant = 50
self.view.setNeedsLayout()
animator.startAnimation()

制約を変更してアニメータを開始する順序は重要です。制約を変更してアニメータを後で残すと、次の再描画サイクルで自動レイアウト再計算が呼び出され、変更はアニメーション化されません。

また、1つのアニメーターは再利用不可能であることを覚えておいてください。実行した後、再実行することはできません。 ですから、アニメーターをインタラクティブなアニメーションを制御するために使用しない限り、アニメーターを維持するのは本当に良い理由ではないと思います。




ワーキングソリューション100% Swift 3.1

私はすべての答えを読んで、私はすべての私のアプリケーションで正しくアニメーション化するために使用しているコードと階層の階層を共有したい、ここでいくつかのソリューションは動作していない、あなたは遅いデバイス例えば、この時点で5を確認する必要があります。

self.view.layoutIfNeeded() // Force lays of all subviews on root view
UIView.animate(withDuration: 0.5) { [weak self] in // allowing to ARC to deallocate it properly
       self?.tbConstraint.constant = 158 // my constraint constant change
       self?.view.layoutIfNeeded() // Force lays of all subviews on root view again.
}



Xcode 8.3.3を使用したSwift 3の作業とテスト済みのソリューション:

self.view.layoutIfNeeded()
self.calendarViewHeight.constant = 56.0

UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.view.layoutIfNeeded()
    }, completion: nil)

self.calendarViewHeightは、customView(CalendarView)に参照される制約です。 self.viewで.layoutIfNeeded()を呼び出し、self.calendarViewではなく

この助けを願っています。




制約アニメーションのコンテキストでは、keyboard_opened通知内で制約をただちにアニメーション化する特定の状況について言及したいと思います。

Constraintは、テキストフィールドからコンテナの上部までの上部スペースを定義しました。 キーボードが開くと、定数を2で割っただけです。

私は、キーボードの通知内で、一貫した滑らかな拘束アニメーションを直接達成することができませんでした。 半分の時間のビューは、アニメーション化されずに新しい位置にジャンプするだけです。

キーボードが開いた結果、レイアウトの追加が発生する可能性がありました。 10msの遅延を持つ単純なdispatch_afterブロックを追加すると、毎回アニメーションが実行されました。ジャンプすることはありません。




Related