2 UIView层动画的应用

在iOS动画开发中,UIView层的动画应该是开发中使用率最高,调用方法最为简单的动画开发方式了。UIView层动画可以将视图控件一些属性的变化以动画的形式展现出来,比如视图控件背景色的渐变效果、视图控件位置改变的移动动画效果和视图控件尺寸改变的缩放动画效果等。

2.1 执行UIView层过渡动画的3个静态方法

UIView层中的过渡动画主要由3个类方法来完成。首先使用Xcode开发工具创建一个工程,命名为UIViewAnimationTest。在ViewController类中声明一个UIView类型的色块,作为测试动画的载体,如下所示:

private var colorView:UIView?

在viewDidLoad方法中进行色块的初始化设置。

 override func viewDidLoad() {
        super.viewDidLoad()
        colorView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        colorView?.backgroundColor = UIColor.red
        self.view.addSubview(colorView!)
    }

上面代码将色块初始化放在了屏幕上坐标为(0,0)的位置。在ViewController类中重写touchesBegan方法来执行移动动画动作。

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        UIView.animate(withDuration: 3) { 
            self.colorView?.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        }
    }

当用户单击屏幕时,系统会调用touchesBegan方法,UIView类的静态方法animate用于创建和执行过渡动画,这个方法中第1个参数负责设置动画执行的时间,第2个参数是一个闭包代码块,开发者需要将执行动画的过渡操作代码放入其中,例如上面代码将色块的位置修改为(100,100),这样当运行工程后单击屏幕,可以看到在3秒内,色块从(0,0)点移动到了(100,100)点。 如下方法也可以进行UIView层过渡动画的创建与执行。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    UIView.animate(withDuration: 3, animations: { 
        self.colorView?.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
    }) { (finished) in
        print("动画完成")
    }
}

这个方法比第1个方法后面多了1个参数,与第1个方法类似,这个方法中第1个参数负责设置动画执行的时间,第2个参数是一个代码块,里面可以添加要执行动画效果的过渡代码,第3个参数也是一个代码代码块,这个代码块中的代码将在动画执行完成之后调用,例如上面代码中打印了完成信息,“完成动画”这句话将在动画执行结束之后被打印。

除了上面介绍的两个创建过渡动画的方法,UIView类还提供了一个更加复杂的方法,代码示例如下:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        UIView.animate(withDuration: 3, delay: 1, options: .curveEaseOut, animations: { 
            self.colorView?.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        }) { (finish) in
            print("动画完成")
        }
}

上面代码中使用的创建执行动画的方法又多了一些参数, 第1个参数设置动画执行的时间; 第2个参数设置动画延时多长时间开始执行,例如上面代码设置为在单击屏幕1秒后再开始执行移动色块的动画; 第3个参数设置动画执行的配置参数,这个参数的意义在后面的小节中会进行详细介绍; 第4个参数为要执行动画动作的代码块。最后一个参数为动画效果完成之后执行的代码块。

2.2 创建UIView层的阻尼动画

在各种各样的iOS动画效果中,阻尼动画十分接近物理场景,在程序开发中应用甚广。例如原生的UITableView在滑动停止的时候会出现减速效果,UITableView的bounce属性可以支持回弹效果等。

这些动画都类似于弹簧,在执行时会有模拟的缓冲或阻尼参数。使用UIView层动画开发技术,开发者也可以十分方便地创建阻尼动画。

在UIViewAnimationTest工程ViewController类中的touchesBegan方法中添加如下代码:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        UIView.animate(withDuration: 3, delay: 1, usingSpringWithDamping: 1, initialSpringVelocity: 20, options: .curveEaseIn, animations: { 
             self.colorView?.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        }, completion: nil)
}

上面代码进行阻尼动画的创建, 在这个方法中第1个参数设置动画执行的时长; 第2个参数设置阻尼动画的阻尼度,这个参数的取值为0~1,越接近1则阻尼度越大。第3个参数设置动画的初速度; 第4个参数设置动画的配置参数; 第5个参数为要执行动画的属性改变代码块;最后一个参数为动画执行完成后的回调代码块。

6.2.3 动画参数配置与组合动画

在上一小节中介绍了animate(WithDuration:delay:options:animations:completion)方法,这个方法中第3个参数用来设置动画的配置参数,这个参数是一个UIViewAnimationOptions类型的枚举,其中定义了许多UIView层动画执行时的基础属性、动画执行时的时间函数和转场动画效果相关的枚举值。UIViewAnimatonOptions集合及其意义如下所示。

public struct UIViewAnimationOptions : OptionSet {
  public static var layoutSubviews: UIViewAnimationOptions { get }////设置子视图随父视图展示动画

    public static var allowUserInteraction: UIViewAnimationOptions { get }//允许在动画执行时用户与其进行交互

    public static var beginFromCurrentState: UIViewAnimationOptions { get } //允许在动画执行时执行新的动画

    public static var `repeat`: UIViewAnimationOptions { get } //设置动画循环执行

    public static var autoreverse: UIViewAnimationOptions { get } //设置动画反向执行,必须和重复执行一起使用

    public static var overrideInheritedDuration: UIViewAnimationOptions { get } //强制动画使用内层动画的时间值

    public static var overrideInheritedCurve: UIViewAnimationOptions { get } //强制动画使用内层动画曲线值

    public static var allowAnimatedContent: UIViewAnimationOptions { get } //设置动画视图实时刷新

   public static var showHideTransitionViews: UIViewAnimationOptions { get } //设置视图切换时隐藏,而不是移除

//这部分属性设置动画播放的时间函数效果
  public static var curveEaseInOut: UIViewAnimationOptions { get } //淡入淡出 首末减速

public static var curveEaseIn: UIViewAnimationOptions { get }//淡入 初始减速

public static var curveEaseOut: UIViewAnimationOptions { get }//淡出 末尾减速

public static var curveLinear: UIViewAnimationOptions { get }//线性 匀速执行
//这部分设置UIView切换效果

public static var transitionFlipFromLeft: UIViewAnimationOptions { get }//从左边切入

public static var transitionFlipFromRight: UIViewAnimationOptions { get }//从右边切入

public static var transitionCurlUp: UIViewAnimationOptions { get }//从上面立体进入

public static var transitionCurlDown: UIViewAnimationOptions { get }//从下面立体进入

public static var transitionCrossDissolve: UIViewAnimationOptions { get }//溶解效果

public static var transitionFlipFromTop: UIViewAnimationOptions { get }//从上面切入

public static var transitionFlipFromBottom: UIViewAnimationOptions { get }//从下面切入

}

UIViewAnimationOptions中实际上定义了3种类型的配置参数,一种定义了视图动画执行时的一些基础属性,例如其子视图是否执行动画、是否允许用户进行交互操作等;一种定义了动画执行时的时间函数,即先快后慢,先慢后快,匀速,淡入淡出等;还有一种定义了UIView转场动画的转场效果,关于UIView的转场动画,后面小节会进行详细介绍。

在前面小节的动画示例中,都只执行了单独的动画效果,使UIView层的动画也可以很好地支持组合动画的开发。组合动画无非两种方式,一种是几种类型的过渡动画同时执行,一种是几种类型的过渡动画依次执行。

关于几种类型动画同时执行的方法很简单,在上面的方法中设置动画的闭包中同时添加多种视图属性的过渡代码即可,如下所示。

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        UIView.animate(withDuration: 3, delay: 1, usingSpringWithDamping: 1, initialSpringVelocity: 20, options: .curveEaseIn, animations: { 
            self.colorView?.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
            self.colorView?.backgroundColor = UIColor.blue
        }, completion: nil)
    }

上面的代码运行效果在3秒内,将色块从原始位置移动到(0,0)点并且尺寸放大为(200,200),与此同时将颜色渐变为蓝色。

关于几种类型的动画依次执行要用到UIView层动画嵌套的方法,示例代码如下:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        UIView.animate(withDuration: 3, animations: { 
            self.colorView?.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        }) { (finish) in
            UIView.animate(withDuration: 2, animations: { 
                self.colorView?.frame = CGRect(x: 100, y: 100, width: 200, height: 200)
            }, completion: { (finish) in
                UIView.animate(withDuration: 1, animations: { 
                    self.colorView?.backgroundColor = UIColor.blue
                })
            })
        }
    }

上面代码执行的效果为:先在3秒内将色块移动到(100,100)的位置,再在2秒内将色块尺寸放大为(200,200),最后在1秒内将色块颜色渐变为蓝色。按原理来讲,UIView层动画可以一直这样嵌套下去。

2.4 UIView层过渡动画支持的属性

在上一小节的示例中,使用UIView层的相关方法进行了控件位置、尺寸、颜色的过渡动画演示。实际上除了上面进行演示的属性之外,还有许多属性支持使用过渡动画,如图2.3中所列举。

图2-3 UIView层动画支持的过渡属性

图2-3中列出的属性意义如下:

  • Frame 为控件的位置与尺寸改变增加过渡动画效果。

  • Bounds 为控件的内部坐标系原点改变与尺寸改变增加过渡动画。

  • Center 为控件的中心点坐标改变增加过渡动画。

  • Transform 为控件的几何变换增加过渡动画。

  • Alpha 为控件的透明度改变增加过渡动画。

  • backgroundColor 为控件的背景色改变增加过渡动画。

  • contentStretch 为UIImageView改变其图片的拉伸方式过程增加过渡动画。

上面介绍的属性中,frame与bounds都是CGRect类型的属性,其在使用CGRectMake()方法进行设置时后两个参数的意义一样,都是设置控件的尺寸大小。但前两个参数意义却相差甚远,frame属性的横纵坐标决定了控件在其父视图上的位置,是对外界而言的,bounds属性的横纵坐标决定了控件内部坐标系的原点,不会影响控件对于其父视图的相对位置,但是会影响它与其内部子视图的相对位置。

控件的transform属性用于设置控件UI上的一些几何变换,例如翻转、旋转、平移、缩放等。例如如下代码将以动画的形式展现控件顺时针旋转90度。

  UIView.animate(withDuration: 3) { 
            self.colorView?.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI_4))
        }

contentStretch属性是针对UIImageView而言的,在为UIImageView设置内容图片时,如果图片过小或者过大,UIImageView会对图片进行整体缩放处理使其充满整个UIImageView控件。

具体图片进行缩放的区域是由contentStretch属性设置的,这个属性也需要设置为CGRect类型,其默认为(0,0,1,1),代表从图片的左上角到右下角都是图片拉伸缩放的区域,即对图片整体进行拉伸缩放操作。如果要设置图片中心点右侧区域和中心点下侧区域为可拉伸缩放的区域,则需将contentStretch属性设置为(0.5,0.5,1,1)。