从UIKit和CoreAnimation开始

通常情况下,iOS app中屏幕上的物体都是UIView对象。它们是矩形的并且有坐标和大小来定义它们在屏幕上的位置和尺寸。UIView是用来构建你的界面的UIKit Framework中基本的界面对象。每个视图都可能伴随着文本、形状或图片绘制。比如说,iOS app中的状态栏是一个长且瘦的视图,处于屏幕的顶端,并且状态栏目中的每个物体(时间、电池指示器、信号强度指示器等等)都是状态栏视图中的其它视图。

这些是为某些功能或特性特定的特殊的UIView对象。包括作为界面按钮的UIButton、用来显示图片的UIImageView、显示文本的UILabel和显示列表的UITableView。你也可以通过绘制任何你想要的东西来完全自定义UIView

这是我的app Interesting for iPhone的截屏和界面中一些视图的分解。


从UIKit和CoreAnimation开始 - 图1


  1. 运营商图像视图(苹果控制)
  2. wifi信号强度视图(苹果控制)
  3. 当前时间视图(苹果控制)
  4. 电池等级视图(苹果控制)
  5. “汉堡包”菜单按钮
  6. 标题栏中的标题标签
  7. 改变子板的按钮
  8. 一个UITableViewCell视图,用来包含UITableView中一行的元素
  9. UILabel中的帖子标题
  10. 评论数量UIButton,由一个评论气泡图和评论的数量组成
  11. 显示帖子URL的UILable
  12. UILabel中显示帖子的点值和子板

如果你不熟悉iOS用户界面开发,看看一些你喜欢的app,看能不能找出界面中所有的视图,以此作为分解你自己设计的练习,这样你就可以学习在代码中构建它们。

UIView对象有很多的职责,其中之一就是事件处理,即响应触摸事件。如果你想的话你界面中的所有视图都可以响应触摸事件,或者你可以指定只有特定的视图会在用户触摸它们时响应。

一个UIView本质上是一个包含内部图形的矩形。在屏幕上布局,靠近或在其他视图的顶部,还可能会有高级的透明效果来整合到一起或者快速绘制。像你想象的一样,让大量的视图在屏幕上移动确实是一个挑战,尤其是在一个小的,低功率的设备上。

这就是为什么苹果公司开发了Core Animation。

Core Animation是一个动画和图形合成框架,用于提高速度和效率。虽然在名字中有动画的单词,不要让它误导你以为它只能做这个;它实际上负责屏幕上显示的所有视图的整体渲染体系结构,可以非常快速地进行透明度计算、图像过滤和视觉效果。它是为iOS创造的,但从OS X10.5开始,也可以在Mac上应用。


从UIKit和CoreAnimation开始 - 图2


要通过GPU管理渲染在屏幕上的图像内容,Core Animation使用CALayer对象作为主力。CALayer负责界面渲染,实际上,UIView对象只是CALayer的简单封装,而Core Animation在苹果公司内部最初名为Layer Kit!当你在屏幕上操作一个UIView的布局或方向的时候,你实际上在移动它的CALayer。Core Animation在硬件层面管理合成并操作你app的界面内容,与显卡对话并精简你界面的渲染,让它变得快速而不迟缓。iOS中所有的动画性能都是由Core Animation框架实现的。


从UIKit和CoreAnimation开始 - 图3


图层可以像视图一样被层级安排来在屏幕上创建一个完整的用户界面。你不是非得要用UIView对象来构建你的界面,也可以使用CALayer对象来代替,像视图一样将它们按照父视图-子视图的类型放置,只不过替换成父图层-子图层。

虽然你可以只是用CALayer不用视图来实现一个app的界面,大部分的iOS开发者仍然都会使用UIView对象而不是直接使用CALayer对象来构建app界面,除非他们在做一些严肃的图形处理或者一次性布局成百上千的图形。如果你需要直接更改图层属性的话随时都可以获取一个视图的图层,比如说,设置一个视图的圆角弧度就是通过操作视图的CALayer属性来完成的。

简单动画

是时候写一些代码了。让我们先添加一个简单的UIView对象到屏幕上并设置它的圆角。我们要把它添加到我们的主窗口上时因为它是一个快速的例子,但在真实的app界面中你需要添加到管理当前界面的视图控制器中。

  1. UIView *redBall = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
  2. redBall.backgroundColor = [UIColor redColor];
  3. redBall.layer.cornerRadius = 50;
  4. [self.window addSubview:redBall];

我们创建了一个新的UIView对象并设置了它的框架来定义它在屏幕上的的X和Y坐标,以及它的宽和高,然后将其添加到屏幕中。我们还将它的背景颜色属性设为了红色。如我前面所说,要让一个视图的角变为圆角,你需要获取它的layer,所以我们设置它的layer.cornerRadius值为50,这是宽度的一半。如果你在你的app的delegate类的-application:didFinishLaunchingWithOptions方法中添加这个代码,就可以在运行后的屏幕上看到它。


从UIKit和CoreAnimation开始 - 图4


这里是和上面一样的功能,但是是Swift而不是Objective-C写的。你可以打开Balls In Swift Xcode工程导出这个例子的Swift版本。

  1. var redBall = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
  2. redBall.backgroundColor = UIColor.redColor()
  3. redBall.layer.cornerRadius = 50
  4. view.addSubview(redBall)

我们在屏幕上有了一个红色的球!很激动,我知道。现在我们让它动起来。

iOS提供了一些内置的技术来创建动画:创建并添加一个CAAnimation到我们之后要讨论的layer中,或者使用简单的基于block的动画方法来动画化UIView的值。让我们创建一个基于block的动画来将圆从1.0扩大到2.0倍,这会让它变成原来的两倍大。

  1. UIView *redBall = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
  2. redBall.backgroundColor = [UIColor redColor];
  3. redBall.layer.cornerRadius = 50;
  4. [self.window addSubview:redBall];
  5. [UIView animateWithDuration:.5 delay:0
  6. options:UIViewAnimationOptionCurveEaseInOut animations:^{
  7. redBall.transform = CGAffineTransformMakeScale(2.0, 2.0);
  8. } completion:NULL];

这个UIView上称为 +animateWithDuration:delay:options:animations:completion: 的类方法时UIView提供的多种动画方法之一。第一个安排,持续时间(duration),被设为半秒,第二个安排,延迟(delay),被设为0。

选项(options)参数让我们设置想要使用的动画类型(它还允许你设置一大串其他选项例如在动画完成后自动反转),所以这个简单的测试中我们选择UIViewAnimationOptionCurveEaseInOut来将时间设为简单的淡入淡出时间曲线。其他的时间曲线选项还有线性、淡入和淡出。

接下来,动画(animations)安排使用了一个block代码作为值,在block中你可以设置你要动画的视图的最终状态。Core Animation会自动在球的当前尺寸值和你的最终值之间更改来产生一个平滑的动画。这一次,我希望动画能最终让球变成两倍大,所以我设置了球的transform属性为一个新值。transform是一个表述了视图中每个像素根据一些线性代码应该改变的值的矩阵。有很多方式来操作一个视图的transform(尺寸、旋转、位置),所以苹果提供了很多函数来改变你感兴趣的值,在我们的例子中,是尺寸。将transform属性设为`CGAffineTransformMakeScale(2.0, 2.0)意味着我们想要其他所有的值都保持不变,除了尺寸,我们想让尺寸变为原来的两倍。

最后,我们不需要在动画完成后运行任何代码,所以我么你设置完成(completion)的安排为NULL。这里是你再次运行代码后会看到的样子。GIF会回到原始的样子但实际上球并不会。


从UIKit和CoreAnimation开始 - 图5


这里是Swift下同样的代码:

  1. UIView.animateWithDuration(0.5, delay: 0,
  2. options: UIViewAnimationOptions.CurveEaseInOut, animations: {
  3. redBall.transform = CGAffineTransformMakeScale(2.0, 2.0)
  4. }, completion: nil)

在block代码块中我们可以改变很多视图相关的属性,它们会在同一个持续时间内一起动画。现在让我们再添加一些值的改变到动画block中来丰富你使用基于block的动画可以操作的内容。

  1. UIView *redBall = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
  2. redBall.backgroundColor = [UIColor redColor];
  3. redBall.layer.cornerRadius = 50;
  4. [self.window addSubview:redBall];
  5. [UIView animateWithDuration:.5 delay:0
  6. options:UIViewAnimationOptionCurveEaseInOut animations:^{
  7. redBall.backgroundColor = [UIColor greenColor];
  8. redBall.transform = CGAffineTransformConcat(
  9. CGAffineTransformMakeScale(2.0, 2.0),
  10. CGAffineTransformMakeTranslation(75, 0));
  11. } completion:NULL];

在我们现在的动画block中,我们做了很多事情。首先,我们将视图的背景色从原始的红色改成了绿色。Core Animation会帮我们修改它并处理中间的颜色。接下来,我们改变了两个关于视图的transform的内容:它的尺寸和平移。平移的更改会将视图上、下、左、右移动。在我们的例子中,我们会将它右移75个像素。我们使用了CGAffineTransformConcat()函数来将两个更改操作合成了一个,这样就可以分配一个简单矩阵转化给视图。你可以手动构建转变矩阵来包含尺寸和平移更改到一个数据结构中,但我发现让iOS来帮我们结合多个单独的转变到一个最终转变会容易一些。


从UIKit和CoreAnimation开始 - 图6


到目前为止有意义吗?围绕转变矩阵的数学有一点复杂和困难,但是苹果让它变得亲近,即使你没有线性代数的背景。动画一个视图的转变矩阵是发动动画最有效的方式之一。