1018

古いアプリを で更新してAdBannerViewいますが、広告がない場合、画面から滑り落ちます。広告があると画面上をスライドします。基本的なもの。

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

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

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

バナーは期待どおりに動きますが、アニメーションはありません。


更新:アニメーションを扱うWWDC 12 トークの自動レイアウトをマスターするためのベスト プラクティスをもう一度見ました。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;
}

ちなみに、私は何度もチェックしましたが、これはメインスレッドで実行されています。

4

12 に答える 12

1776

2 つの重要な注意事項:

  1. layoutIfNeededアニメーション ブロック内で呼び出す必要があります。Apple は、保留中のすべてのレイアウト操作が完了していることを確認するために、アニメーション ブロックの前に 1 回呼び出すことを実際に推奨しています。

  2. 制約が付加されている子ビューではなく、親ビュー(例: )で具体的に呼び出す必要があります。self.viewそうすることで、制約を変更したビューに制約されている可能性のある他のビューをアニメーション化するなど、制約されたすべてのビューが更新されます (たとえば、ビュー B はビュー A の下部に接続されており、ビュー A の上部オフセットを変更したばかりで、ビュー B が必要です)。アニメーション化する)

これを試して:

Objective-C

- (void)moveBannerOffScreen {
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = -32;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen { 
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = 0;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = TRUE;
}

スイフト3

UIView.animate(withDuration: 5) {
    self._addBannerDistanceFromBottomConstraint.constant = 0
    self.view.layoutIfNeeded()
}
于 2012-09-30T19:00:58.860 に答える
117

提供された回答に感謝しますが、もう少し進めていただければ幸いです。

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

[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には、まだ足りない部分がたくさんありますが、一読の価値があります。私自身、単純で微妙な折りたたみアニメーション (0.2 秒の長さ)を使用UISwitchして、サブビューを のペアで切り替える の一部としてこれを使用しています。UITextFieldサブビューの制約は、上記のように UIView サブクラスの updateConstraints メソッドで処理されます。

于 2014-01-22T00:35:50.280 に答える
81

layoutIfNeeded通常、アニメーション ブロック内で制約を更新して呼び出すだけで済みます。これは、 の.constantプロパティの変更、NSLayoutConstraint削除制約の追加 (iOS 7)、または.active制約のプロパティの変更 (iOS 8 & 9) のいずれかです。

サンプルコード:

[UIView animateWithDuration:0.3 animations:^{
    // Move to right
    self.leadingConstraint.active = false;
    self.trailingConstraint.active = true;

    // Move to bottom
    self.topConstraint.active = false;
    self.bottomConstraint.active = true;

    // Make the animation happen
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

サンプルセットアップ:

Xcode プロジェクトなので、サンプル アニメーション プロジェクトです。

論争

制約をアニメーション ブロックの前に変更する必要があるのか​​、それとも内部で変更する必要があるのか​​について、いくつか質問があります(以前の回答を参照)。

以下は、iOS を教えている Martin Pilkington と Auto Layout を書いた Ken Ferry の間の Twitter での会話です。Ken は、アニメーション ブロックの外側で定数を変更しても現在は機能する可能性がありますが、安全ではなく、実際にはアニメーション ブロックで変更する必要があると説明しています。 https://twitter.com/kongtomorrow/status/440627401018466305

アニメーション:

サンプルプロジェクト

ビューをアニメーション化する方法を示す簡単なプロジェクトを次に示します。Objective C を使用しており.active、いくつかの制約のプロパティを変更してビューをアニメーション化します。 https://github.com/shepting/SampleAutoLayoutAnimation

于 2015-11-13T00:33:16.050 に答える
41
// 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];
}];
于 2013-12-23T14:29:37.213 に答える
16

ストーリーボード、コード、ヒント、いくつかの落とし穴

他の答えは問題ありませんが、これは最近の例を使用して、制約をアニメーション化する際のいくつかのかなり重要な落とし穴を強調しています。次のことに気付く前に、多くのバリエーションを経験しました。

対象とする制約をクラス変数にして、強い参照を保持します。Swift では、遅延変数を使用しました。

lazy var centerYInflection:NSLayoutConstraint = {
       let temp =  self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
        return temp!
}()

いくつかの実験の後、制約が定義されている2つのビューの上のビュー(別名スーパービュー)から制約を取得する必要があることに気付きました。以下の例では (MNGStarRating と UIWebView の両方が、制約を作成している 2 つのタイプの項目であり、self.view 内のサブビューです)。

フィルター連鎖

Swift のフィルター メソッドを利用して、変曲点として機能する目的の制約を分離します。はるかに複雑になることもありますが、フィルターはここでうまく機能します。

Swift を使用した制約のアニメーション化

Nota Bene - この例は、ストーリーボード/コード ソリューションであり、ストーリーボードで既定の制約が作成されていることを前提としています。その後、コードを使用して変更をアニメーション化できます。

正確な基準でフィルタリングし、アニメーションの特定の変曲点に到達するプロパティを作成すると仮定します (もちろん、複数の制約が必要な場合は、配列をフィルタリングしてループスルーすることもできます)。

lazy var centerYInflection:NSLayoutConstraint = {
    let temp =  self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
    return temp!
}()

....

今度いつか...

@IBAction func toggleRatingView (sender:AnyObject){

    let aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)

    self.view.layoutIfNeeded()


    //Use any animation you want, I like the bounce in springVelocity...
    UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.75, options: [.CurveEaseOut], animations: { () -> Void in

        //I use the frames to determine if the view is on-screen
        if CGRectContainsRect(self.view.frame, self.ratingView.frame) {

            //in frame ~ animate away
            //I play a sound to give the animation some life

            self.centerYInflection.constant = aPointAboveScene
            self.centerYInflection.priority = UILayoutPriority(950)

        } else {

            //I play a different sound just to keep the user engaged
            //out of frame ~ animate into scene
            self.centerYInflection.constant = 0
            self.centerYInflection.priority = UILayoutPriority(950)
            self.view.setNeedsLayout()
            self.view.layoutIfNeeded()
         }) { (success) -> Void in

            //do something else

        }
    }
}

多くの間違ったターン

これらのメモは、実際には私が自分のために書いた一連のヒントです。私はすべての禁止事項を個人的に、そして苦痛を伴いました。うまくいけば、このガイドが他の人を救うことができます.

  1. zPositioning に注意してください。何も起こっていないように見える場合は、他のビューを非表示にするか、ビュー デバッガーを使用してアニメーション ビューを見つける必要があります。ストーリーボードの xml でユーザー定義のランタイム属性が失われ、(作業中に) アニメーション ビューがカバーされるケースさえ見つけました。

  2. ドキュメント (新旧を問わず)、クイック ヘルプ、およびヘッダーを読むために、常に少し時間を割いてください。Apple は、AutoLayout の制約をより適切に管理するために、多くの変更を加え続けています (スタック ビューを参照してください)。または少なくともAutoLayout Cookbook。最善の解決策が古いドキュメント/ビデオにある場合があることに注意してください。

  3. アニメーションの値をいじって、他の animateWithDuration バリアントの使用を検討してください。

  4. 他の定数への変更を決定する基準として特定のレイアウト値をハードコードしないでください。代わりに、ビューの場所を決定できる値を使用してください。CGRectContainsRect一例です

  5. 必要に応じて、制約定義に参加しているビューに関連付けられたレイアウト マージンを使用することをためらわないでください let viewMargins = self.webview.layoutMarginsGuide
  6. 必要のない作業は行わないでください。ストーリーボードに制約があるすべてのビューには、プロパティ self.viewName.constraints に制約が付加されています
  7. 制約の優先順位を 1000 未満に変更します。ストーリーボードで優先順位を 250 (低) または 750 (高) に設定しました。(1000 の優先度をコード内の何かに変更しようとすると、1000 が必要なためアプリがクラッシュします)
  8. すぐに activateConstraints と activateConstraints を使用しようとしないことを検討してください (これらには場所がありますが、これらを使用して学習したり、ストーリーボードを使用している場合は、おそらくやりすぎを意味します。以下に示すように場所があります)。
  9. コードに新しい制約を実際に追加する場合を除き、 addConstraints / removeConstraints を使用しないことを検討してください。ほとんどの場合、必要な制約を使用してストーリーボードにビューをレイアウトし (ビューを画面外に配置)、コードで、ストーリーボードで以前に作成した制約をアニメーション化してビューを移動することがわかりました。
  10. 新しい NSAnchorLayout クラスとサブクラスで制約を構築するのに多くの時間を費やしました。これらは問題なく機能しますが、必要なすべての制約がストーリーボードに既に存在していることに気付くまでにしばらく時間がかかりました。コードで制約を作成する場合は、このメソッドを使用して制約を集約するのが最も確実です。

ストーリーボードを使用するときに避けるべき解決策の簡単なサンプル

private var _nc:[NSLayoutConstraint] = []
    lazy var newConstraints:[NSLayoutConstraint] = {

        if !(self._nc.isEmpty) {
            return self._nc
        }

        let viewMargins = self.webview.layoutMarginsGuide
        let minimumScreenWidth = min(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height)

        let centerY = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.webview.centerYAnchor)
        centerY.constant = -1000.0
        centerY.priority = (950)
        let centerX =  self.ratingView.centerXAnchor.constraintEqualToAnchor(self.webview.centerXAnchor)
        centerX.priority = (950)

        if let buttonConstraints = self.originalRatingViewConstraints?.filter({

            ($0.firstItem is UIButton || $0.secondItem is UIButton )
        }) {
            self._nc.appendContentsOf(buttonConstraints)

        }

        self._nc.append( centerY)
        self._nc.append( centerX)

        self._nc.append (self.ratingView.leadingAnchor.constraintEqualToAnchor(viewMargins.leadingAnchor, constant: 10.0))
        self._nc.append (self.ratingView.trailingAnchor.constraintEqualToAnchor(viewMargins.trailingAnchor, constant: 10.0))
        self._nc.append (self.ratingView.widthAnchor.constraintEqualToConstant((minimumScreenWidth - 20.0)))
        self._nc.append (self.ratingView.heightAnchor.constraintEqualToConstant(200.0))

        return self._nc
    }()

これらのヒントのいずれか、または layoutIfNeeded を追加する場所などのより単純なヒントのいずれかを忘れた場合、ほとんどの場合何も起こりません: その場合、次のような中途半端な解決策になる可能性があります:

注意 - 以下の AutoLayout セクションと元のガイドを読んでください。これらのテクニックを使用してダイナミック アニメーターを補う方法があります。

UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1.0, options: [.CurveEaseOut], animations: { () -> Void in

            //
            if self.starTopInflectionPoint.constant < 0  {
                //-3000
                //offscreen
                self.starTopInflectionPoint.constant = self.navigationController?.navigationBar.bounds.height ?? 0
                self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)

            } else {

                self.starTopInflectionPoint.constant = -3000
                 self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
            }

        }) { (success) -> Void in

            //do something else
        }

    }

AutoLayout Guide のスニペット (2 番目のスニペットは OS X を使用するためのものです)。ところで-これは、私が見る限り、現在のガイドにはありません。 好ましい技術は進化し続けています。

自動レイアウトによる変更のアニメーション化

自動レイアウトによるアニメーションの変更を完全に制御する必要がある場合は、プログラムで制約を変更する必要があります。基本的なコンセプトは iOS と OS X で同じですが、いくつかの小さな違いがあります。

iOS アプリでは、コードは次のようになります。

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

OS X では、レイヤーに基づくアニメーションを使用する場合、次のコードを使用します。

[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
     [context setAllowsImplicitAnimation: YES];
     // Make all constraint changes here
     [containerView layoutSubtreeIfNeeded];
}];

レイヤーに基づくアニメーションを使用していない場合は、制約のアニメーターを使用して定数をアニメーション化する必要があります。

[[constraint animator] setConstant:42];

視覚的によりよく学ぶ人は、Apple からのこの初期のビデオをチェックしてください。

細心の注意を払う

多くの場合、ドキュメントには、より大きなアイデアにつながる小さなメモやコードが含まれています。たとえば、動的アニメーターに自動レイアウト制約を適用することは大きなアイデアです。

幸運を祈ります。フォースがあなたと共にありますように。

于 2015-12-11T01:49:37.490 に答える
5

コンストレイントをアニメーション化しようとしていたのですが、適切な説明を見つけるのは簡単ではありませんでした。

[self.view layoutIfNeeded];他の回答が言っていることは完全に真実です:内部 で呼び出す必要がありますanimateWithDuration: animations:NSLayoutConstraintただし、もう 1 つの重要な点は、アニメーション化するすべてのポインターを用意することです。

GitHub で例を作成しました

于 2014-11-25T08:24:33.327 に答える
4

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 では呼び出しませんでした

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

于 2017-07-19T12:17:43.800 に答える
3

これについての記事があり ます:

その中で、彼は次のようにコーディングしました。

- (void)handleTapFrom:(UIGestureRecognizer *)gesture {
    if (_isVisible) {
        _isVisible = NO;
        self.topConstraint.constant = -44.;    // 1
        [self.navbar setNeedsUpdateConstraints];  // 2
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded]; // 3
        }];
    } else {
        _isVisible = YES;
        self.topConstraint.constant = 0.;
        [self.navbar setNeedsUpdateConstraints];
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded];
        }];
    }
}

それが役に立てば幸い。

于 2013-09-13T08:35:13.060 に答える
1

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

制約により、テキストフィールドからコンテナーの上部までの上部スペースが定義されました。キーボードを開くと、定数を 2 で割ります。

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

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

于 2016-04-15T15:54:27.237 に答える