迅速に制約をアニメーション化しようとしています


242

タップしたときに幅を拡大したいUITextFieldがあります。制約を設定し、左側の制約が右側でアニメーション化しようとしている制約よりも優先順位が低いことを確認しました。

これが私が使用しようとしているコードです。

  // move the input box
    UIView.animateWithDuration(10.5, animations: {
        self.nameInputConstraint.constant = 8
        }, completion: {
            (value: Bool) in
            println(">>> move const")
    })

これは機能しますが、即座に発生するようで、動きはありません。何も欠落していないことを確認するために10秒に設定しようとしましたが、同じ結果が得られました。

nameInputConstraintは、IBからクラスに接続するためにドラッグして制御する制約の名前です。

よろしくお願いします!


回答:


665

最初に制約を変更してから、更新をアニメーション化する必要があります。

self.nameInputConstraint.constant = 8

スウィフト2

UIView.animateWithDuration(0.5) {
    self.view.layoutIfNeeded()
}

スウィフト3、4、5

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

2
誰かがこれがどのように/なぜ機能するのか説明できますか?UIViewでanimationWithDurationを呼び出すと、UIViewから継承して物理値を変更するクラスがアニメーション化されますか?
nipponese 2016年

3
あなたが言及したアニメーションAPIは 、ビューとレイヤーのプロパティをアニメーション化するために使用されます。ここでは、レイアウト変更をアニメーション化する必要があります。これが、レイアウト制約の定数を変更するために必要なことです。定数を変更するだけでは何も起こりません。
ムンディ2016年

2
@Jackyおそらく、これを強い変数と弱い変数に関してObjective-Cの推論と間違えているでしょう。Swiftクロージャーは異なります。クロージャーが終了selfすると、クロージャーで使用されているオブジェクトを保持しているオブジェクトはありません。
ムンディ

2
私の場合は機能しません、それは最初に起こります、何をすべきか
Shakti

13
スーパービューでlayoutIfNeededを呼び出さなければならないことに気づくのに1時間かかりました...
Nominalista

28

SWIFT 4.x:

self.mConstraint.constant = 100.0
UIView.animate(withDuration: 0.3) {
        self.view.layoutIfNeeded()
}

補完ありの例:

self.mConstraint.constant = 100
UIView.animate(withDuration: 0.3, animations: {
        self.view.layoutIfNeeded()
    }, completion: {res in
        //Do something
})

2
動作していませんこれを実行しましたUIView.animate(withDuration:10、delay:0、options:.curveEaseInOut、animations:{self.leftViewHeightConstraint.constant = 200 self.leftView.layoutIfNeeded()}、completion:nil)
saurabhgoyal795

1
あなたは、制約と変更する必要があり、その後アニメーションを。
JaredH

このコードスニペットが解決策となる場合もありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
Narendra Jadhav

上記の行は私の日を作りました:)ありがとう@Hadzi
Nrv

18

view.layoutIfNeeded()ビューのサブビューにのみ適用されることを指摘することは非常に重要です。

したがって、ビューの制約をアニメーション化するには、ビューとアニメーションのスーパービューで次のように呼び出すことが重要です。

    topConstraint.constant = heightShift

    UIView.animate(withDuration: 0.3) {

        // request layout on the *superview*
        self.view.superview?.layoutIfNeeded()
    }

次のような単純なレイアウトの例:

class MyClass {

    /// Container view
    let container = UIView()
        /// View attached to container
        let view = UIView()

    /// Top constraint to animate
    var topConstraint = NSLayoutConstraint()


    /// Create the UI hierarchy and constraints
    func createUI() {
        container.addSubview(view)

        // Create the top constraint
        topConstraint = view.topAnchor.constraint(equalTo: container.topAnchor, constant: 0)


        view.translatesAutoresizingMaskIntoConstraints = false

        // Activate constaint(s)
        NSLayoutConstraint.activate([
           topConstraint,
        ])
    }

    /// Update view constraint with animation
    func updateConstraint(heightShift: CGFloat) {
        topConstraint.constant = heightShift

        UIView.animate(withDuration: 0.3) {

            // request layout on the *superview*
            self.view.superview?.layoutIfNeeded()
        }
    }
}

Swift 4で高さの制約を更新すると、ビューでlayoutIfNeededを呼び出すときに機能しましたが、スーパービューでは機能しませんでした。本当に役に立ちました、ありがとう!
Alekxos

これは正解です。スーパービューで呼び出さない場合、ビューは新しい場所にジャンプします。これは非常に重要な違いです。
ブライアンディマー

11

Swift 5およびiOS 12.3では、ニーズに応じて、問題を解決するために次の3つの方法のいずれかを選択できます。


#1。UIViewanimate(withDuration:animations:)クラスメソッドの使用

animate(withDuration:animations:) 次の宣言があります:

指定された期間を使用して、1つ以上のビューへの変更をアニメーション化します。

class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Void)

以下のプレイグラウンドコードはanimate(withDuration:animations:)、自動レイアウト制約の一定の変化をアニメーション化するために可能な実装を示しています。

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        UIView.animate(withDuration: 2) {
            self.view.layoutIfNeeded()
        }
    }

}

PlaygroundPage.current.liveView = ViewController()

#2。UIViewPropertyAnimatorinit(duration:curve:animations:)初期化子とstartAnimation()メソッドを使用する

init(duration:curve:animations:) 次の宣言があります:

組み込みのUIKitタイミングカーブを使用してアニメーターを初期化します。

convenience init(duration: TimeInterval, curve: UIViewAnimationCurve, animations: (() -> Void)? = nil)

ショー以下の遊び場コードの可能な実装init(duration:curve:animations:)startAnimation()自動レイアウト制約の定数の変化をアニメーション化するためです。

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        let animator = UIViewPropertyAnimator(duration: 2, curve: .linear, animations: {
            self.view.layoutIfNeeded()
        })
        animator.startAnimation()
    }

}

PlaygroundPage.current.liveView = ViewController()

#3。UIViewPropertyAnimatorrunningPropertyAnimator(withDuration:delay:options:animations:completion:)クラスメソッドの使用

runningPropertyAnimator(withDuration:delay:options:animations:completion:) 次の宣言があります:

すぐにアニメーションの実行を開始するanimatorオブジェクトを作成して返します。

class func runningPropertyAnimator(withDuration duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions = [], animations: @escaping () -> Void, completion: ((UIViewAnimatingPosition) -> Void)? = nil) -> Self

以下のプレイグラウンドコードはrunningPropertyAnimator(withDuration:delay:options:animations:completion:)、自動レイアウト制約の一定の変化をアニメーション化するために可能な実装を示しています。

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 2, delay: 0, options: [], animations: {
            self.view.layoutIfNeeded()
        })
    }

}

PlaygroundPage.current.liveView = ViewController()

1
そのような素晴らしい答え!
バシュタ2018年

@Imanou素晴らしい答え、詳細に感謝!おそらくオプションの短い説明を追加し、違いは何ですか?私にとって本当に助かりますし、私が他の人を選んだ理由について他の人にもきっと一文あると思います。
rayepps 2018年

3

私の場合、カスタムビューのみを更新しました。

// DO NOT LIKE THIS
customView.layoutIfNeeded()    // Change to view.layoutIfNeeded()
UIView.animate(withDuration: 0.5) {
   customViewConstraint.constant = 100.0
   customView.layoutIfNeeded() // Change to view.layoutIfNeeded()
}

-1

これを見る

ビデオではself.view.layoutIfNeeded()、次のように追加する必要があると述べています。

UIView.animate(withDuration: 1.0, animations: {
       self.centerX.constant -= 75
       self.view.layoutIfNeeded()
}, completion: nil)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.