如何使用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
是一个可以配置为以各种方式控制动画的对象。 这是一个完整的示例,演示:
- 开始动画
- 暂停动画
- 找出已完成动画的分数并将幻灯片值设置为该分数
- 使用滑块擦除动画
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)? }