尝试在swift中动画约束

我有一个UITextField,我想在点击时放大它的宽度。我设置了约束条件,并确保左边的约束条件优先级比右边的约束条件低。

这是我正在尝试使用的代码。

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

这是有效的,但它似乎只是瞬间发生,似乎没有任何运动。我试着把它设置为10秒,以确保我没有错过任何东西,但我得到了同样的结果。

nameInputConstraint是约束的名称,我控制拖动连接到我的类从IB。

146829 次浏览
你需要首先改变约束,然后动画更新。
这应该在父视图中
self.nameInputConstraint.constant = 8

斯威夫特2

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

斯威夫特,3,4,5

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

在我的例子中,我只更新了自定义视图。

// 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()
}

在Swift 5和iOS 12.3中,根据您的需要,您可以选择以下3种方式之一来解决您的问题。


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

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()的一个可能实现,以动画自动布局约束的不断变化。

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

下面的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()

SWIFT 4及以上:

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
})

非常重要的一点是,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()
}
}
}

视频说你只需要像下面这样添加self.view.layoutIfNeeded():

UIView.animate(withDuration: 1.0, animations: {
self.centerX.constant -= 75
self.view.layoutIfNeeded()
}, completion: nil)

我想分享我的解决方案,在我不工作的情况下,我的约束设计如下

view1WidthLayout = NSLayoutConstraint(item: view1,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: .width,
multiplier: 1,
constant: 20)

当我试图在动画之前设置常数时

view1WidthLayout.constant += 20

它马上就凝固了,所以对我不起作用。

我改变了约束属性的定义

view1WidthLayout = view1.widthAnchor.constraint(equalToConstant: 20)

然后它对我有用