如何使用自动布局的UIViewPropertyAnimator?

Gol*_*Joe 5 animation uiview ios autolayout swift

我可以在网上找到所有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()
Run Code Online (Sandbox Code Playgroud)

它显然增加了某种约束?我在执行动画之前和之后记录了容器视图的约束:

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)>]
Run Code Online (Sandbox Code Playgroud)

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

vac*_*ama 18

您可以使用UIViewPropertyAnimator带有自动布局,但需要修改,而不是视图的框架的限制,并呼吁layoutIfNeeded()封闭的内部.

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

在Storyboard中创建一个视图.创建约束以centerX将视图定位为等于超视图的前沿.创建一个@IBOutlet到该约束并调用它centerXConstraint.同样,创建一个将视图centerYConstraint定位为centerY等于其超级视图顶部的视图.这两个约束都应该有constant = 0.

创建@IBOutlet约束条件:

@IBOutlet weak var centerXConstraint: NSLayoutConstraint!
@IBOutlet weak var centerYConstraint: NSLayoutConstraint!
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

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

在Storyboard中创建一个视图(例如redView)并@IBOutlet在代码中创建一个视图:

@IBOutlet weak var redView: UIView!
Run Code Online (Sandbox Code Playgroud)

创建@IBOutlet用于控制视图位置的约束

// Outlets to the constraints set in the Storyboard
@IBOutlet weak var topConstraint: NSLayoutConstraint!
@IBOutlet weak var leadingConstraint: NSLayoutConstraint!
Run Code Online (Sandbox Code Playgroud)

然后是时候动画了:

// 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()
Run Code Online (Sandbox Code Playgroud)

那么这比旧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)
    }
}
Run Code Online (Sandbox Code Playgroud)