UIKit:如何使用以编程方式创建的约束为约束更改设置动画?
UIKit: how to animate a constraint change with a programmatically created constraint?
我已经按照 this answer 从 XIB 文件中实例化了一个视图:
extension UIView {
class func fromNib(named: String? = nil) -> Self {
let name = named ?? "\(Self.self)"
guard let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil) else {
fatalError("missing expected nib named: \(name)")
}
guard let view = nib.first as? Self else {
fatalError("view of type \(Self.self) not found in \(nib)")
}
return view
}
}
现在我想创建一个根据内容自适应高度的视图:
class CustomTopView: UIView {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subtitleLabel: UILabel!
static func instance(withTitle title: NSAttributedString, subtitle: NSAttributedString) -> CustomTopView {
let customTopView = CustomTopView.fromNib()
customTopView.titleLabel.attributedText = title
customTopView.subtitleLabel.attributedText = subtitle
return customTopView
}
}
我想做的是从屏幕顶部动画显示此视图,就像通知弹出窗口一样。我写了这个:
extension UIView {
func showCustomTopView(withTitle title: NSAttributedString, subtitle: NSAttributedString) {
let customTopView = CustomTopView.instance(withTitle: title, subtitle: subtitle)
addSubview(customTopView)
customTopView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customTopView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
customTopView.constraint(equalTo: customTopView.trailingAnchor, constant: 20.0),
])
customTopView.layoutIfNeeded() // *
NSLayoutConstraint.activate([customTopView.topAnchor.constraint(equalTo: topAnchor, constant: -customTopView.bounds.height)])
}
}
我这里有两个问题。最主要的是我不知道如何从这里执行我想要的动画,以便视图最终可见。我在最后一个 NSLayoutConstraint.activate(...)
:
之后尝试了这个
UIView.animate(withDuration: 0.5) {
self.topAnchor.constraint(equalTo: customTopView.topAnchor, constant: 100.0).isActive = true
self.layoutIfNeeded()
}
但是弹出窗口没有按预期显示,而是从屏幕顶部 100px 处开始,然后上升并消失。我做错了什么?
此外,我不确定标有 // *
的行:我写这行是为了获取正确的 customTopView
高度,但我不确定这是正确的方法.
感谢您的帮助!
一种方法:
- 创建 一个 约束设置
customTopView
的底部在视图的顶部上方
- 创建一个 second 约束设置
customTopView
的顶部低于视图的顶部
- 添加
customTopView
作为子视图,激活第一个约束
- 通过停用第一个约束并激活第二个约束,将
customTopView
设置为动画
这是您的 showCustomTopView(...)
扩展的修改版本,使用 UILabel
作为 customTopView
-- 应该可以很好地与您的 CustomTopView.instance(withTitle: ...)
:
extension UIView {
func showCustomTopView(withTitle title: NSAttributedString, subtitle: NSAttributedString) {
let customTopView = UILabel()
customTopView.text = title.string
customTopView.backgroundColor = .green
addSubview(customTopView)
customTopView.translatesAutoresizingMaskIntoConstraints = false
let g = self.safeAreaLayoutGuide
// constraint: Bottom of customTopView to Top of self Minus 8-pts
let cvBot = customTopView.bottomAnchor.constraint(equalTo: topAnchor, constant: -8.0)
// constraint: Top of customTopView to Top of self Plus 8-pts
let cvTop = customTopView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0)
NSLayoutConstraint.activate([
customTopView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
customTopView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20.0),
// activate cvBot, so customTopView is 8-pts above the top of self
cvBot,
])
// execute this async, so the initial view position is set
DispatchQueue.main.async {
// deactivate the Bottom constraint
cvBot.isActive = false
// activate the Top constraint
cvTop.isActive = true
// animate it into view
UIView.animate(withDuration: 0.5, animations: {
self.layoutIfNeeded()
})
}
}
}
我已经按照 this answer 从 XIB 文件中实例化了一个视图:
extension UIView {
class func fromNib(named: String? = nil) -> Self {
let name = named ?? "\(Self.self)"
guard let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil) else {
fatalError("missing expected nib named: \(name)")
}
guard let view = nib.first as? Self else {
fatalError("view of type \(Self.self) not found in \(nib)")
}
return view
}
}
现在我想创建一个根据内容自适应高度的视图:
class CustomTopView: UIView {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subtitleLabel: UILabel!
static func instance(withTitle title: NSAttributedString, subtitle: NSAttributedString) -> CustomTopView {
let customTopView = CustomTopView.fromNib()
customTopView.titleLabel.attributedText = title
customTopView.subtitleLabel.attributedText = subtitle
return customTopView
}
}
我想做的是从屏幕顶部动画显示此视图,就像通知弹出窗口一样。我写了这个:
extension UIView {
func showCustomTopView(withTitle title: NSAttributedString, subtitle: NSAttributedString) {
let customTopView = CustomTopView.instance(withTitle: title, subtitle: subtitle)
addSubview(customTopView)
customTopView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customTopView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
customTopView.constraint(equalTo: customTopView.trailingAnchor, constant: 20.0),
])
customTopView.layoutIfNeeded() // *
NSLayoutConstraint.activate([customTopView.topAnchor.constraint(equalTo: topAnchor, constant: -customTopView.bounds.height)])
}
}
我这里有两个问题。最主要的是我不知道如何从这里执行我想要的动画,以便视图最终可见。我在最后一个 NSLayoutConstraint.activate(...)
:
UIView.animate(withDuration: 0.5) {
self.topAnchor.constraint(equalTo: customTopView.topAnchor, constant: 100.0).isActive = true
self.layoutIfNeeded()
}
但是弹出窗口没有按预期显示,而是从屏幕顶部 100px 处开始,然后上升并消失。我做错了什么?
此外,我不确定标有 // *
的行:我写这行是为了获取正确的 customTopView
高度,但我不确定这是正确的方法.
感谢您的帮助!
一种方法:
- 创建 一个 约束设置
customTopView
的底部在视图的顶部上方 - 创建一个 second 约束设置
customTopView
的顶部低于视图的顶部 - 添加
customTopView
作为子视图,激活第一个约束 - 通过停用第一个约束并激活第二个约束,将
customTopView
设置为动画
这是您的 showCustomTopView(...)
扩展的修改版本,使用 UILabel
作为 customTopView
-- 应该可以很好地与您的 CustomTopView.instance(withTitle: ...)
:
extension UIView {
func showCustomTopView(withTitle title: NSAttributedString, subtitle: NSAttributedString) {
let customTopView = UILabel()
customTopView.text = title.string
customTopView.backgroundColor = .green
addSubview(customTopView)
customTopView.translatesAutoresizingMaskIntoConstraints = false
let g = self.safeAreaLayoutGuide
// constraint: Bottom of customTopView to Top of self Minus 8-pts
let cvBot = customTopView.bottomAnchor.constraint(equalTo: topAnchor, constant: -8.0)
// constraint: Top of customTopView to Top of self Plus 8-pts
let cvTop = customTopView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0)
NSLayoutConstraint.activate([
customTopView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
customTopView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20.0),
// activate cvBot, so customTopView is 8-pts above the top of self
cvBot,
])
// execute this async, so the initial view position is set
DispatchQueue.main.async {
// deactivate the Bottom constraint
cvBot.isActive = false
// activate the Top constraint
cvTop.isActive = true
// animate it into view
UIView.animate(withDuration: 0.5, animations: {
self.layoutIfNeeded()
})
}
}
}