试图快速动画约束


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连接到我的类的约束的名称。

感谢您的帮助!


Answers:


664

您需要先更改约束,然后为更新设置动画。

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继承并更改物理值的任何类设置动画吗?
日本,2016年

3
您提到的动画API用于为 视图和图层的属性设置动画。在这里,我们需要动画化布局变化。这就是更改布局约束的常量所需要的-仅更改常量不会执行任何操作。
蒙迪

2
@Jacky也许您在使用Objective-C推理时会误以为是强变量和弱变量。Swift的闭包是不同的:闭包完成后,闭包中的对象就不会保留self
Mundi

2
在我的情况下不起作用,它首先发生,该怎么办
Shakti,

13
我花了一个小时才注意到,我必须在Superview上致电layoutIfNeeded ...
Nominalista

27

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()},完成操作:nil)
saurabhgoyal795

1
您必须先更改约束,然后进行动画处理。
JaredH

尽管此代码段可能是解决方案,但提供说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。
Narendra Jadhav

上一行让我开心:)谢谢@Hadzi
Nrv

17

指出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,根据您的需要,您可以选择以下三种方式之一来解决您的问题。


#1。使用UIViewanimate(withDuration:animations:)class方法

animate(withDuration:animations:) 具有以下声明:

使用指定的持续时间对一个或多个视图进行动画更改。

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

下面的Playground代码显示了的可能实现方式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)

下面的Playground代码显示了一种可能的实现,init(duration:curve:animations:)startAnimation()为Auto Layout约束的不变变化设置动画。

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:)class方法

runningPropertyAnimator(withDuration:delay:options:animations:completion:) 具有以下声明:

创建并返回一个动画师对象,该对象立即开始运行其动画。

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

下面的Playground代码显示了的可能实现方式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
很好的答案!
巴什塔

@Imanou很好的答案,欣赏细节!可能会添加选项的简短说明,区别是什么?这对我真的很有帮助,我敢肯定别人会对我为什么选择一个而不是另一个说一句话。
rayepps

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.