我可以在线找到UIViewPropertyAnimator
的所有示例都使用通过设置框架而不是使用自动布局布局的视图,这是视图通常的布局方式。使用自动布局时,视图会动画到我想要的位置,但之后我不确定如何将其设置回“模型”位置。
// Move a view that is constrained to the center of its superview
UIViewPropertyAnimator(duration: 1, curve: .linear) {
testView.center = CGPoint(x: 10, y: 10)
}.startAnimation()
它显然增加了某种约束?我在执行动画之前和之后记录了容器视图的约束:
CONSTRAINTS BEFORE:
view: [<NSLayoutConstraint:0x618000094f00 UIView:0x7fd764004050.centerX == UIView:0x7fd75fd00000.centerX (active)>, <NSLayoutConstraint:0x618000092de0 UIView:0x7fd764004050.centerY == UIView:0x7fd75fd00000.centerY (active)>]
CONSTRAINTS AFTER:
view: [<NSLayoutConstraint:0x618000094f00 UIView:0x7fd764004050.centerX == UIView:0x7fd75fd00000.centerX (active)>, <NSLayoutConstraint:0x618000092de0 UIView:0x7fd764004050.centerY == UIView:0x7fd75fd00000.centerY (active)>, <NSLayoutConstraint:0x6100000922a0 'UIView-Encapsulated-Layout-Height' UIView:0x7fd75fd00000.height == 667 (active)>, <NSAutoresizingMaskLayoutConstraint:0x610000092570 h=-&- v=-&- 'UIView-Encapsulated-Layout-Left' UIView:0x7fd75fd00000.minX == 0 (active, names: '|':UIWindow:0x7fd75fc07110 )>, <NSAutoresizingMaskLayoutConstraint:0x610000092890 h=-&- v=-&- 'UIView-Encapsulated-Layout-Top' UIView:0x7fd75fd00000.minY == 0 (active, names: '|':UIWindow:0x7fd75fc07110 )>, <NSLayoutConstraint:0x610000091670 'UIView-Encapsulated-Layout-Width' UIView:0x7fd75fd00000.width == 375 (active)>]
这里发生了什么?我该如何处理使用自动布局定位的视图的动画? Apple是否鼓励用户回到基于框架的布局?
答案 0 :(得分:17)
您可以将UIViewPropertyAnimator
与自动布局一起使用,但您需要修改约束而不是视图的框架,并在闭包内调用layoutIfNeeded()
。
第一个示例:通过修改约束constant
在Storyboard中创建一个视图。创建约束以将视图的centerX
定位为等于超视图的前沿。为该约束创建@IBOutlet
并将其命名为centerXConstraint
。同样,创建一个centerYConstraint
,使您的视图的centerY
等于其超级视图的顶部。这两个约束都应该有constant = 0
。
为约束创建@IBOutlet
:
@IBOutlet weak var centerXConstraint: NSLayoutConstraint!
@IBOutlet weak var centerYConstraint: NSLayoutConstraint!
将约束的constant
值设置为新位置,然后在view.layoutIfNeeded()
内调用UIViewPropertyAnimator
:
// Make sure view has been laid out
view.layoutIfNeeded()
// Set new X and Y locations for the view
centerXConstraint.constant = 50
centerYConstraint.constant = 80
UIViewPropertyAnimator(duration: 1, curve: .easeInOut) {
self.view.layoutIfNeeded()
}.startAnimation()
第二个示例:通过激活新约束进行动画
在故事板中创建一个视图(例如redView
)并在代码中为其创建@IBOutlet
:
@IBOutlet weak var redView: UIView!
为控制视图位置的约束创建@IBOutlet
// Outlets to the constraints set in the Storyboard
@IBOutlet weak var topConstraint: NSLayoutConstraint!
@IBOutlet weak var leadingConstraint: NSLayoutConstraint!
然后是什么时候动画:
// Make sure view has been laid out
view.layoutIfNeeded()
// Deactivate the old constraints
topConstraint.isActive = false
leadingConstraint.isActive = false
// Active new constraints that move view to the bottom right
redView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
redView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// Animate the change
UIViewPropertyAnimator(duration: 1, curve: .easeInOut) {
self.view.layoutIfNeeded()
}.startAnimation()
那么这比旧的
UIView
动画块更好?
UIViewPropertyAnimator
是一个可以配置为以各种方式控制动画的对象。以下是一个完整的示例:
class ViewController: UIViewController {
@IBOutlet weak var redView: UIView!
@IBOutlet weak var centerXConstraint: NSLayoutConstraint!
@IBOutlet weak var centerYConstraint: NSLayoutConstraint!
@IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
slider.isHidden = true
}
var myAnimator: UIViewPropertyAnimator?
@IBAction func startAnimation(_ sender: UIButton) {
view.layoutIfNeeded()
centerXConstraint.isActive = false
centerYConstraint.isActive = false
redView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
redView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
myAnimator = UIViewPropertyAnimator(duration: 10, curve: .easeInOut) {
self.view.layoutIfNeeded()
}
myAnimator?.startAnimation()
}
@IBAction func pauseAnimation(_ sender: UIButton) {
guard let myAnimator = myAnimator else { return }
myAnimator.pauseAnimation()
print("The animation is \(String(format: "%.1f", myAnimator.fractionComplete * 100))% complete")
slider.value = Float(myAnimator.fractionComplete)
slider.isHidden = false
}
@IBAction func scrub(_ sender: UISlider) {
myAnimator?.fractionComplete = CGFloat(sender.value)
}
}
答案 1 :(得分:0)
通过github和完整教程的应用程序示例:
http://eon.codes/blog/2018/05/15/Animating-with-constraints/
class AnimVC:UIViewController{
lazy var square:Square = createSquare()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
_ = square
}
}
extension AnimVC{
func createSquare() -> Square{
let square = Square()
square.backgroundColor = .orange
self.view.addSubview(square)
_ = {//Define constraints for Square
square.translatesAutoresizingMaskIntoConstraints = false
let anchor = Constraint.anchor(square, to: view, align: .centerCenter, alignTo: .centerCenter)
let size = Constraint.size(square, size: CGSize(width:100,height:100))
square.anchor = anchor
square.size = size
NSLayoutConstraint.activate([anchor.x,anchor.y,size.w,size.h])
}()
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
square.addGestureRecognizer(tap)
return square
}
@objc func handleTap(_ sender:UITapGestureRecognizer){
let newConstraint = {
guard let oldAnchorConstraint = self.square.anchor else {fatalError("err posConstraint not available")}
NSLayoutConstraint.deactivate([oldAnchorConstraint.x])
let newAnchorConstraint = Constraint.anchor(self.square, to: self.view, align: .topLeft, alignTo: .topLeft, offset: CGPoint(x:0,y:0))
NSLayoutConstraint.activate([newAnchorConstraint.x])
self.square.anchor?.x = newAnchorConstraint.x
}
let anim = UIViewPropertyAnimator(duration: 0.3, curve: .easeOut, animations: {
newConstraint()// ⚠️️ Set the new constraint goal
self.view.layoutIfNeeded()//⚠️️ Ask the parent view to update its layout
})
anim.startAnimation()
}
}
class Square:UIView{
var anchor:(x:NSLayoutConstraint,y:NSLayoutConstraint)?
var size:(w:NSLayoutConstraint,h:NSLayoutConstraint)?
}