如何使用自动布局的UIViewPropertyAnimator?

时间:2017-06-03 07:01:04

标签: ios swift animation uiview autolayout

我可以在线找到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是否鼓励用户回到基于框架的布局?

2 个答案:

答案 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是一个可以配置为以各种方式控制动画的对象。以下是一个完整的示例:

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

    Scrubbing animation demo

    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)

我为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)?
}