Auto Layout Animation

Chapter 6, Introduction to Auto Layout

主要介绍一些基本的auto layout布局,没什么好说的。

Chapter 7, Animating Constraints

本章主要是介绍如果对约束添加动画。和普通的对UIView属性做动画不同,对约束的动画经常是创建一个新的约束替换老的约束,然后让Auto Layout在两个约束间做动画。

1 动态改变布局

假如存在一个高度的约束

    @IBOutlet weak var someViewHeightConstraint: NSLayoutConstraint!

现在需要对高度做动画,更改为高度为 200
代码如下:

    someViewHeightConstraint.constant = 200

如果只是单纯的执行上面的代码,视图的高度会变成200,但是不会有动画效果。此时需要调用 layoutIfNeeded() 方法,因为当改变约束constant变量的值的时候,iOS还没有机会去更新此时的布局,此时iOS只是把当前的约束标记为的状态。

添加如下代码,使约束添加动态效果:

    someViewHeightConstraint.constant = 200
    UIView.animate(withDuration: 1.0) {
        self.view.layoutIfNeeded()
    }
2 检查和对约束做动画

对于一些不能通过类似StoryBoard做可视化链接或者不想添加可视化的约束,可以通过代码创建。

在一些情况如果想在runtime改变一个视图的约束的值,而有没有该约束的引用,可以通过遍历视图的constraints属性

    someView.constraints

比如,如果想对一个在 container view 内水平居中的 titleLabel 动态修改约束值。可以做如下尝试:

        titleLabel.superview?.constraints.forEach { constraint in
            if constraint.firstItem === titleLabel && constraint.firstAttribute == .centerX {
                constraint.constant =  -100.0 
                return
            }
        }

上面的代码实现的是水平居中约束左移100。

当然也可以在创建约束的时候,对该约束的属性Identifier设置一个字符串标示。比如对如上的titleLabel水平居中约束添加标示为 titleCenterX。那么上面的代码可以替换为:

        titleLabel.superview?.constraints.forEach { constraint in
            if constraint.identifier == "titleCenterX" {
                constraint.constant = -100
                return
            }
        }
3 替换已有约束做动画

目前上面内容都是结束的对已经的约束改变属性constant的值做动画
如果要实现一些更加复杂的动画效果呢,这时可能创建一个新的约束,然后替换已经存在的约束可能会实现起来更加的容易。

在开始前需要知道的是,对于某个view已经存在的一个约束比如constraintA
如果做如下处理:

constraintA.isActive = false

这会使当前视图层级把constraintA移除,如果此时外面又没有对constraintA的引用,那么constraintA将会被内存删除。

对于一个在视图内居中的view来说,它的表现形式为:
someView.centerX = multiple * superView.centerX + constant

multiple 是倍数,一般等于1
constant 移动距离,一般等于0

在之前介绍的水平居中的titleLabel左移100的则可如下表示:
titleLabel.centerX = 1.0*superView.centerX + -100.0

如果titleLabel需要一个0.5倍水平居中的约束替换已有的水平居中约束,则可创建如下新约束:

        let newConstraint = NSLayoutConstraint(item: titleLabel,
                                               attribute: .centerX,
                                               relatedBy: .equal,
                                               toItem: titleLabel.superview!,
                                               attribute: .centerX,
                                               multiplier: 0.5,
                                               constant: 0)
        newConstraint.identifier = "titleCenterX"
        newConstraint.isActive = true

newConstraint 的identifiers属性设置了一个字符串标示,这样是为了更好的调试。
设置 newConstraint.isActive 告诉iOS在当前布局使用该约束。

如果一次性创建了大量的约束,没必要对每一个约束动都设置一下
someConstraint.isActive = true
使用 NSLayoutConstraint.activate([NSLayoutConstraint]) 会更好的管理代码。

4 整理

1: 如果要对一个约束动态该constant属性值,记得在 UIView.animate(...)里调用
self.view.layoutIfNeeded()告诉iOS需要重新布局了。

2: 对于一个已经存在的约束,可以通过 IBOutlets ,代码引用, 或者简单粗暴的遍历查看 identifier 或者属性详情查找。

3:对于一个约束,
设置属性 isActive = false 会让该约束的view调用 view.removeConstraint(someConstraint);
设置属性 isActive = true 会让该约束的view调用 view.addConstraint(someConstraint)。

4:最后一个小知识

一个需求,创建一个图片 imageView, imageView的初始约束为 屏幕水平居中,在屏幕外的正下方。想要对约束做动画移动到屏幕中间

此时如果改变约束后调用 UIView.animate( self.view.layoutIfNeeded() ) 会看到imageView 直接从屏幕左上角(0, 0)处做动画到屏幕中间。
因为虽然设置了初始约束位置,但是视图一直没有执行初始化布局,所以imageView的初始化布局位置会是默认左上角(0,0)

修复问题: 在 UIView.animate( self.view.layoutIfNeeded() ) 之前再次调用一下 self.view.layoutIfNeeded() ,告诉iOS更新一下初始化布局。

        // set imageView initial layout
        self.view.layoutIfNeeded()

        // animating update imageView layout
        UIView.animate(withDuration: 1.0, animations: {
            self.view.layoutIfNeeded()
        })

你可能感兴趣的:(Auto Layout Animation)