Swift - 自定义 UIView 中的动画约束
Swift - Animate Constraints in Custom UIView
我正在尝试在自定义 UIView
中设置约束动画。结束布局是正确的,但我无法让 UIView.animate
正常工作,而且我看到每个人都从视图控制器调用 self.view.layoutIfNeeded()
,所以这也许是我的问题。我在自定义视图本身上调用 self.layoutIfNeeded()
。如果层次结构很重要,我有如下内容:ViewController > TableView > Cell > View > AnimationView.
class AnimationView: UIView {
// MARK: - Properties
let ratioMultiplier: CGFloat = 0.28
var listLeadingConstraint: NSLayoutConstraint!
var listTrailingConstraint: NSLayoutConstraint!
var checkmarkLeadingConstraint: NSLayoutConstraint!
var checkmarkTrailingConstraint: NSLayoutConstraint!
// MARK: - UI Components
lazy var listImageView: UIImageView = {
let image = UIImage(named: "list.small")
let result = UIImageView(image: image)
return result
}()
lazy var checkmarkImageView: UIImageView = {
let image = UIImage(named: "checkmark.small")
let result = UIImageView(image: image)
return result
}()
// MARK: - Inits
init() {
super.init(frame: .zero)
setupView()
}
required init?(coder: NSCoder) {
return nil
}
// MARK: - Methods
func playAnimation() {
// 1. change constraints
listLeadingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
listTrailingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
checkmarkLeadingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
checkmarkTrailingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
self.layoutIfNeeded()
// 2. animate constraints
UIView.animate(withDuration: 3.0) {
self.layoutIfNeeded()
}
}
func setupView() {
// 1. add subviews
addSubview(listImageView)
addSubview(checkmarkImageView)
// 2. add constraints
ConstraintHelper.anchor(view: checkmarkImageView, options: [.top, .bottom])
checkmarkLeadingConstraint = checkmarkImageView.leadingAnchor.constraint(equalTo: leadingAnchor)
checkmarkTrailingConstraint = checkmarkImageView.trailingAnchor.constraint(equalTo: trailingAnchor)
checkmarkLeadingConstraint.constant -= checkmarkImageView.frame.size.width * ratioMultiplier
checkmarkTrailingConstraint.constant -= checkmarkImageView.frame.size.width * ratioMultiplier
NSLayoutConstraint.activate([checkmarkLeadingConstraint, checkmarkTrailingConstraint])
ConstraintHelper.anchor(view: listImageView, options: [.top, .bottom])
listLeadingConstraint = listImageView.leadingAnchor.constraint(equalTo: leadingAnchor)
listTrailingConstraint = listImageView.trailingAnchor.constraint(equalTo: trailingAnchor)
NSLayoutConstraint.activate([listLeadingConstraint, listTrailingConstraint])
}
}
其他信息:playAnimation()
是从 table 视图上的 cellForRow
调用的,我也尝试使用 self.superview?.layoutIfNeeded()
调用但没有成功。 ConstraintHelper
调用 translatesAutoresizingMaskIntoConstraints
并且约束正常工作,所以不用担心。任何帮助都会很棒。
成功了。将约束更改移动到动画块修复它,尽管这与那里的大多数帖子形成对比,后者说在动画块之前进行约束更改,准备时调用一次 layoutIfNeeded(),然后调用第二个 layoutIfNeeded()在动画块中强制更新和动画。为什么这种行为在我的代码中有所不同,我仍然不确定...
func playAnimation() {
// update constraints & animate
UIView.animate(withDuration: 3.0) {
listLeadingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
listTrailingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
checkmarkLeadingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
checkmarkTrailingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
self.layoutIfNeeded()
}
}
您的初始 post 不起作用的原因是您在约束更改后调用了您的第一个 layoutIfNeeded()
。当你的动画块被执行时,根本没有任何变化可以被动画化。
改变动画块内外的约束并不重要。
常用步骤为:
- 在您的约束可能影响的最根视图上调用
layoutIfNeeded()
。在您的情况下, self
可以,因为所有涉及的约束都是针对子视图的。在某些情况下,您可能会更改自身视图的约束,然后您需要在其父视图上调用 layoutIfNeeded
。
- 更改约束:修改常量 属性、activate/deactivate 约束等
- 在与第一步相同的视图上的动画块中调用
layoutIfNeeded()
。
请尝试以下替代方法:
func playAnimation() {
// Request a layout to clean pending changes for the view if there's any.
self.layoutIfNeeded()
// Make your constraint changes.
listLeadingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
listTrailingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
checkmarkLeadingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
checkmarkTrailingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
UIView.animate(withDuration: 3.0) {
// Let your constraint changes update right now.
self.layoutIfNeeded()
}
}
我正在尝试在自定义 UIView
中设置约束动画。结束布局是正确的,但我无法让 UIView.animate
正常工作,而且我看到每个人都从视图控制器调用 self.view.layoutIfNeeded()
,所以这也许是我的问题。我在自定义视图本身上调用 self.layoutIfNeeded()
。如果层次结构很重要,我有如下内容:ViewController > TableView > Cell > View > AnimationView.
class AnimationView: UIView {
// MARK: - Properties
let ratioMultiplier: CGFloat = 0.28
var listLeadingConstraint: NSLayoutConstraint!
var listTrailingConstraint: NSLayoutConstraint!
var checkmarkLeadingConstraint: NSLayoutConstraint!
var checkmarkTrailingConstraint: NSLayoutConstraint!
// MARK: - UI Components
lazy var listImageView: UIImageView = {
let image = UIImage(named: "list.small")
let result = UIImageView(image: image)
return result
}()
lazy var checkmarkImageView: UIImageView = {
let image = UIImage(named: "checkmark.small")
let result = UIImageView(image: image)
return result
}()
// MARK: - Inits
init() {
super.init(frame: .zero)
setupView()
}
required init?(coder: NSCoder) {
return nil
}
// MARK: - Methods
func playAnimation() {
// 1. change constraints
listLeadingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
listTrailingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
checkmarkLeadingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
checkmarkTrailingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
self.layoutIfNeeded()
// 2. animate constraints
UIView.animate(withDuration: 3.0) {
self.layoutIfNeeded()
}
}
func setupView() {
// 1. add subviews
addSubview(listImageView)
addSubview(checkmarkImageView)
// 2. add constraints
ConstraintHelper.anchor(view: checkmarkImageView, options: [.top, .bottom])
checkmarkLeadingConstraint = checkmarkImageView.leadingAnchor.constraint(equalTo: leadingAnchor)
checkmarkTrailingConstraint = checkmarkImageView.trailingAnchor.constraint(equalTo: trailingAnchor)
checkmarkLeadingConstraint.constant -= checkmarkImageView.frame.size.width * ratioMultiplier
checkmarkTrailingConstraint.constant -= checkmarkImageView.frame.size.width * ratioMultiplier
NSLayoutConstraint.activate([checkmarkLeadingConstraint, checkmarkTrailingConstraint])
ConstraintHelper.anchor(view: listImageView, options: [.top, .bottom])
listLeadingConstraint = listImageView.leadingAnchor.constraint(equalTo: leadingAnchor)
listTrailingConstraint = listImageView.trailingAnchor.constraint(equalTo: trailingAnchor)
NSLayoutConstraint.activate([listLeadingConstraint, listTrailingConstraint])
}
}
其他信息:playAnimation()
是从 table 视图上的 cellForRow
调用的,我也尝试使用 self.superview?.layoutIfNeeded()
调用但没有成功。 ConstraintHelper
调用 translatesAutoresizingMaskIntoConstraints
并且约束正常工作,所以不用担心。任何帮助都会很棒。
成功了。将约束更改移动到动画块修复它,尽管这与那里的大多数帖子形成对比,后者说在动画块之前进行约束更改,准备时调用一次 layoutIfNeeded(),然后调用第二个 layoutIfNeeded()在动画块中强制更新和动画。为什么这种行为在我的代码中有所不同,我仍然不确定...
func playAnimation() {
// update constraints & animate
UIView.animate(withDuration: 3.0) {
listLeadingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
listTrailingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
checkmarkLeadingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
checkmarkTrailingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
self.layoutIfNeeded()
}
}
您的初始 post 不起作用的原因是您在约束更改后调用了您的第一个 layoutIfNeeded()
。当你的动画块被执行时,根本没有任何变化可以被动画化。
改变动画块内外的约束并不重要。
常用步骤为:
- 在您的约束可能影响的最根视图上调用
layoutIfNeeded()
。在您的情况下,self
可以,因为所有涉及的约束都是针对子视图的。在某些情况下,您可能会更改自身视图的约束,然后您需要在其父视图上调用layoutIfNeeded
。 - 更改约束:修改常量 属性、activate/deactivate 约束等
- 在与第一步相同的视图上的动画块中调用
layoutIfNeeded()
。
请尝试以下替代方法:
func playAnimation() {
// Request a layout to clean pending changes for the view if there's any.
self.layoutIfNeeded()
// Make your constraint changes.
listLeadingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
listTrailingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
checkmarkLeadingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
checkmarkTrailingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
UIView.animate(withDuration: 3.0) {
// Let your constraint changes update right now.
self.layoutIfNeeded()
}
}