首页 > 代码库 > UI动画高级篇(一)

UI动画高级篇(一)

UI动画高级篇(一)

一、引入·Xcode设置圆角按钮

1.在何处设置圆角属性

Xcode的属性检查器中不能直接设置按钮为圆角,因为圆角属性是程序运行时显示的。故应在身份检查器的user defined runtime attributes(用户定义的运行时属性)处增加KeyPath并通过KVC添加Button的layer.cornerRadius(层的角半径)来设置圆角。

注意:user defined runtime attributes无报错机制,我们须保障正确的属性名书写和Type选择。

技术分享

 

 

(图1 Xcode检查器分类)

2.按钮上有无激活masksToBounds属性的必要

首先我们来看下cornerRadius属性的官方描述和定义,如图。

技术分享

(图2 cornerRadius属性的官方描述)

可知,只设置layer.cornerRadius就可以显示圆角了。

那为何有人想多激活一个layer的masksToBounds属性呢。我们可以再看下masksToBounds属性的官方描述和定义。

 技术分享

(图3 masksToBounds属性的官方描述)

即是说,若给控件激活了masksToBounds属性,该属性就会生成一个和控件大小一样的隐式罩子,严密地覆在控件上,若设置了控件为圆角,则该罩子也是圆角罩。以button为例:

就是该罩子无论如何都严丝合缝地覆盖在button上,超出罩子的都裁边。

 


 那么此人添加masksToBounds的理由就是,防止程序猿在button上又放一个子控件时,如该子控件有超出button的bounds的部分,那么子控件就会覆盖button显示,导致设按钮为圆角毫无意义。而激活maskToBounds后,因为罩子以外范围的东西都被裁边。故就算button思维子控件千变万化,最后看到的仍是圆角button的样子。但没有必要这么做的原因如下:


 

UIButton上是无法添加子控件的。故不存在UIButton的子控件超出本控件显示的问题,因为UIButton根本不可能有子控件!

当然,就算不能添加子控件,但可以在button的layer上添加一个或多个sublayer,那样也可能造成边界溢出的现象。好吧,如果你非要这么添加,那就加上masksToBounds=YES以避免这种智障情况吧。

3.何时使用masksToBounds才恰当

答案是在可添加子控件的控件上都可使用。如UIView。下面举个例子。

技术分享

(图4 添加一个UIView和它的一个子视图UIButton)

(1)我们将layer.masksToBounds的对勾去掉,运行后视图如下

技术分享

(图5 关闭UIView的masksToBounds属性后的运行效果)

(2)我们将layer.masksToBounds的对勾打上,运行后视图如下

技术分享

(图6 打开UIView的masksToBounds属性后的运行效果)

可知,这就是为何有时在某些控件上要激活masksToBounds,防止出现一些bug的原因。但UIButton控件是不用多此一举的。

4.什么是mask属性

masksToBounds属性的说明中,提到了mask属性,mask是个CALayer类对象,即它是一个层。

该属性官方说明如下图

技术分享

(图7 mask属性的官方说明)

技术分享

(图8 mask属性的官方注释)

(1)这里提到第一个名词叫阿尔法通道。阿尔法通道是一个8位的灰度通道,该通道用256级灰度来记录图像中的透明度信息,定义透明、不透明和半透明区域,其中黑表示透明,白表示不透明,灰表示半透明。也就说,阿尔法通道可看做过滤器,即便是彩色图,通过阿尔法通道也会过滤为灰色。

(2)这里提到的第二个名词是"transparent" 和 "opaque",这两个词都可翻译成中文的“透明”或“不透明”,出现这种情况的原因是中西方文化差异。中国人说透明就是可见,不透明就是不可见,西方说透明反而是不可见,说透明反而是可见。只是可不可见的指代对象不同。好比你在一堵完全透明的玻璃前,是看不到玻璃本身的,走过去就会撞到玻璃上,因为透明到了极限,这东西就对你不可见了,但是你可以看见玻璃后面的东西。故对玻璃我们可以说它透明,也可说它不透明,但编程中,我们一般遵照西方思维,说玻璃是不透明的,即玻璃不可见。


transparent若译为中文的“可见”,指的是能看见透明物的后面。若译为中文的“不可见”,指的是看不见透明物本身。

opaque若译为中文的“可见”,指的是能看见透明物。若译为中文的“不可见”,指的是看不见透明物的后面。


 原句:

Fully or partially opaque pixels allow the underlying content to show through but fully transparent pixels block that content.

语义读者可自行揣摩。

(3)同一层的mask属性和masksToBounds可以共存,且效果叠加。前者是在本层显示范围内过滤,后者是把超出本层bounds的子层裁边。二者其实没有相互影响的。

(4)但一个细节是,按理说,mask和masksToBounds都是生成一个“罩子”,为何后者要用mask的复数形式(即masks-ToBounds)来定义属性名,而前者只用单数呢。其实想想也就明白,mask永远只要一层就够了,一层就足够过滤它所覆盖的层的内容和背景了。但masksToBounds是要裁边子层,子层可能有很多,所以,由masksToBounds生成的罩子肯定不止一个,可以把maskToBounds生成的罩子想成一个立体的空水桶,所以用masks就不足为奇了。

(5)下图验证默认的mask和masksToBounds都为空。

技术分享

 

(图9 mask和masksToBounds属性都默认为空)

二、起止型动画/动画三剑客/老式动画

 1.起止型动画有哪三个基本方法

开始动画、动画时长、提交动画

技术分享

 

(图10 起止型动画三要素)

动画块执行是一个线性的过程,故也可以用此方法逐渐改变控件的透明度。

如,要使一button逐渐从不透明改变到0.5的透明度,只需要在起止方法间加上如下代码:

button.alpha=0.5;

2.起止型动画在控件移动中为何不能处理点击事件

因为[UIView commitAnimations];后面的代码不代表动画结束。

我们看起来控件是在慢慢移动,但是实际上,控件是瞬间移动到动画结束位置,故而点击移动中的控件是没有任何效果的。测试如下:

技术分享

(图11 起止型动画不能在控件移动中处理点击事件)

 3.若我们要在动画结束后进行操作,该用什么方法

我们想在动画结束后一顿操作,又不能在[UIView commitAnimations];方法之后写,那么怎么实现功能呢。这时我们可以用一个废弃的代理方法。

因为是代理方法,所以我们必须先设置代理。

[UIView setAnimationDelegate:self];

然后再在本类中实现如下图代码:

技术分享

(图12 废弃的代理方法)

因为此方法已经废弃,Xcode 6之后都不能用了,官方文档上也已经查不到此方法了。

当然还有第二种方法也可以完成此项操作,后面会提到。

4.起止型动画的其他常用方法

(1)让动画从当前状态开始。

 [UIView setAnimationBeginsFromCurrentState:YES];

用法举例:

若我们用一个UIButton控制一个UIView,第一次点击button让view垂直下落300像素,第二次点击button让view水平右移200像素。

若是按钮动画时间设得较长而我们点击较快时,view不会中途转变方向,而是出现一些难以理解的幺蛾子。但激活animationBeginsFromCurrentState属性后,动画就会从当前状态开始移动,当我们在下落途中就点击了第二次,那么view就会从点击时下落到的地方开始,完成第二次动画。如下图

技术分享

(图13 正常运行和脑残运行路线)

(2)让动画传参

[UIView setAnimationWillStartSelector:@selector(begin)];[UIView setAnimationDidStopSelector:@selector(over)];

这两个方法是为动画设置一个启动器和停止器。当动画开始时,启动器可以调用我们自己写的begin方法,动画结束时,停止器可以调用我们自己写的over方法。通过第二个方法,我们也可以实现在动画结束后一顿操作。我们可以在方法中打个输出来观察。

2016-09-27 14:19:16.969 AnimationTest[4111:139250] 动画开始2016-09-27 14:19:17.968 AnimationTest[4111:139250] 动画结束

4.这里将官方文档的相关方法部分翻译

技术分享

 

 (图13 beginAnimations:context:方法)

技术分享

(图13 commitAnimations方法)

技术分享

(图14 setAnimationStartDate:方法)

技术分享

(图15 setAnimationsEnabled:方法)

技术分享

(图16 setAnimationDelegate:方法)

技术分享

 

(图17 setAnimationWillStartSelector:方法)

技术分享

(图18 setAnimationDidStopSelector:方法)

技术分享

(图19 setAnimationDuration:方法)

技术分享

(图19 setAnimationDelay:方法)

技术分享

(图19 setAnimationCurve:方法)

技术分享

(图20 setAnimationRepeatCount:方法)

技术分享

(图21 setAnimationRepeatAutoreverses:方法)

技术分享

(图22 setAnimationBeginsFromCurrentState:方法)

技术分享

(图23 setAnimationTransition:forView:cache:方法)

 技术分享

(图24 areAnimationsEnabled属性)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

UI动画高级篇(一)