如何使用UIViewPropertyAnimator自动布局?

我在网上找到的所有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: [, ] CONSTRAINTS AFTER: view: [, , , , , ] 

这里发生了什么? 我应该如何处理使用自动布局定位的视图的动画? Apple是否鼓励用户回归基于框架的布局?

您可以将UIViewPropertyAnimator自动布局一起使用,但您需要修改约束而不是视图的框架,并在闭包内调用layoutIfNeeded()

第一个例子:通过修改约束constant来动画

在故事板中创建一个视图。 创建约束以将视图的centerX定位为等于centerX的前沿。 为该约束创建一个@IBOutlet并将其centerXConstraint 。 同样,创建一个centerYConstraint ,将视图的centerYConstraint定位为等于其centerY的顶部。 这两个约束都应该具有constant = 0

创建约束的@IBOutlet

 @IBOutlet weak var centerXConstraint: NSLayoutConstraint! @IBOutlet weak var centerYConstraint: NSLayoutConstraint! 

将约束的constant量值设置为新位置,然后在UIViewPropertyAnimator调用view.layoutIfNeeded()

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

第二个示例:通过激活新约束进行动画处理

在Storyboard中创建一个视图(例如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是一个可以配置为以各种方式控制动画的对象。 这是一个完整的示例,演示:

  1. 开始动画
  2. 暂停动画
  3. 找出已完成动画的分数并将幻灯片值设置为该分数
  4. 使用滑块擦除动画

擦洗动画演示

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

我为swift 4.1做了一个教程

通过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)? }