先看一下这个教程的效果

Core Graphics 是iOS上一个很酷的API。作为开发者,你会使用它来个性化你的UI设计,用上一些很棒的效果 – 而且不需要一个设计师参与制作! 但是对于很多iOS开发者来说,Core Graphics 一开始会让人恐惧,因为它是一个很庞大的API,而且在开发过程中会遇到很多小困难。所以在本篇教程中,我们将解开Core Graphics的神秘面纱,并且用一系列的练习去一步步展示 – 现在我们从使用Core Graphics来美化一个table view开始吧!
我们将要制作的table view的效果和上面的截屏一样。这么特别的设计灵感来自Bills,一个由 PoweryBase开发的设计精美的app。这是个相当酷的app,你可以看下!在该教程系列的第一篇中,我们将使用Core Graphics去制作一个精美的table view cell。
我们将讲解Core Graphics的入门知识,如何去填充绘制矩形,如何绘制颜色渐变效果,以及如何处理1像素宽的线问题。
在接下来的教程系列中,将继续美化app – Table view的header,footer和收尾工作。现在让我们开始接触有趣的Core Graphics吧!
开始
开始前,让我们先创建一个有table view模版的工程。
打开Xcode,选择Navigation-based Application 模版,命名工程为 “CoolTable”。编译运行工程,确保一个空白的table view出现。
现在让我们添加一些例子数据到table view中。打开RootViewController.h文件,根据以下内容做代码修改:
|
我们在这里添加了两个数组,在接下来为两个table view section中的内容添加字符串。现在切换到RootViewController.m文件并根据以下内容做修改:
|
我们在这里添加了两个数组,接下来会为两个table view section中的内容添加字符串。现在切换到RootViewController.m文件并根据以下内容做修改:
很好 – 现在我们有一些例子数据了!编译运行工程,你将看到以下画面:
然而,当你上下滚动table view的整个section内容时,header会“浮”在上面:
这是一个标准的设定为“plain”风格的table view行为。然而,有了这个“plain”风格设定后,我们并不想让header像这样“浮”在上面 – 我们想让它们像row(行)一样是一个单元行。有“grouped”风格设定的table view就是我们想要的!
现在切换到RootViewController.xib文件,点击xib中的Table View,设置Style参数为“Grouped”:
很好!保存RootViewController.xib的设定,返回工程,现在我们会看到一个有很多内容项(只是看起来)的table view:
我们使用Core Graphics去美化它吧!但是在之前,我们还需要讨论下我们想要的效果。
Table View 风格分析
为了获得我们想要的效果,我们将在table view的三个不同section中绘制:table header,cells和footer:
在本篇教程中,我们将开始绘制cells,现在在让我们进一步看下想要的效果:
注意以下对上述效果图的分析:
- cells具有从白色渐变到浅灰色的效果。
- 每一个cell的边界周围都有白色边来突出它(除了最后的cell,只在两边有白色边)。
- 每一个cell之间都有一条灰色的线来分隔它们(出了最后的cell)。
- 页面在实际的cell边界处会呈现出一点锯齿状,和来自header的“下摆”页面对齐。
另外 – 它模拟的是当有光线以一定角度照射在iPhone顶部时的情形(一般房间里面会有光线的)。要达到这种效果,顶部需要提高亮度(白色),底部需要有阴影(灰色)。你会在很多UI设计里面看到这种效果,接下来的教程系列里面也会看到!
所以要绘制cell,我们需要知道如何使用Core Graphics去绘制渐变效果和一些线条。应该会相当简单,对吧?我们开始吧!
你好,Core Graphics!
无论什么时候你想在iOS上做个性化绘制,你绘制的代码需要放在UIView内部。有一个特殊的方法叫drawRect,你可以把所有的绘制代码都放到里面。
我们先创建一个“Hello,World”的红色view,然后把它设置为table view cell 的背景,确保正常运作。
现在先选择 “Groups & Files”下面的”Classes”分组,前往菜单栏的”FileNew File…”,选择 iOSCocoa Touch Class,Objective-C class,确保”Subclass of UIView”被选上,然后点击下一步。
命名文件为 “CustomCellBackground.m”,确保”Also create CustomCellBackground.h”被选中,然后点击 “Finish”。
我们不需要对头文件做修改,直接切换到CustomCellBackground.m文件,根据以下代码做出修改:
|
好的,这里有一些新内容,先来一点点解释下。
在第一行,我们调用了叫做UIGraphicsGetCurrentContext()的方法来获得Core Graphics Context,在接下来的方法中还会用到它。按照我的理解,context就是我们在上面绘制的“画布”。
按照这种情况,我们的“画布”就是view,但是你也可以获得其他类型的context,比如一个屏幕以外的缓冲区,你可以在稍后把它转变成图像。
关于context有趣的一点是,他们是状态性的。这表示当你调用了函数去改变一些属性,比如改变填充颜色,填充颜色会一直维持那个颜色状态,直到你改变了颜色为止。
事实上,这就是我们在第三行代码所作的 – 我们用CGContextSetFillColorWithColor函数去把填充颜色设置为红色,以供接下来填充形状颜色的时候使用。
你也许会注意到当你调用这个方法时,我们不能提供UIColor给函数做参数 – 而是要使用CGColorRef。幸运的是,其实很容易把UIColor转换成CGColor,只需访问UIColor的CGColor属性。
最后的一行代码,我们调用了一个方法去用颜色填充提供的方框(使用之前在context中设定好的填充颜色)。对于方框,我们传入了view的bounds值。
既然我们有一个红色view了,让我们把它设置为table view cell的背景吧!根据以下代码对RootViewController.m文件做修改:
|
我们在这里做的是将每个cell的backgroundView和selectedBackgroundView在新的CustomCellBackground类中创建。我们还把cell的文本标签text label的背景颜色设定为clear,让我们的背景可以显露出来。
编译运行程序,你将看到以下画面:
太好了,我们可以用Core Graphics去绘制了!不管你信不信,我们已经学会了一些重要的技术 – 如何获取一个context去绘制,如何改变填充颜色,如何用颜色去填充方框。你可以用这种方法去制作精美的UI了!
现在我们要进一步深入,学习其中一种最有用的技术去制作精美的UI:颜色渐变!
绘制渐变效果
我们将要在本工程中绘制很多渐变效果,所以让我们添加颜色渐变代码到一个辅助函数中。这样我们就不需要在工程中重复编写这部分的代码了!确保你选中了”Groups & Files”下面的”Classes”分组,前往菜单项的”FileNew File…”,选择 iOSCocoa Touch Class,Objective-C class,确保”Subclass of NSObject”选项被选中,然后点击Next,命名文件为“Common.m”,确保”Also create Common.h”选项被选中,然后点击”Finish”。
现在使用以下代码替换掉Common.h文件的内容:
|
我们在这里不是定义一个类 – 我们只是定义一个公共方法。
现在切换到Common.m文件,使用以下代码替换掉原来的内容:
|
这个函数里面有很多技术点,现在分两部分去解释。我们先从刚才写的部分开始,它创建了接下来要绘制的渐变效果。
首先我们需要去做的是获得将要绘制的渐变效果的color space。你可以用color space去做很多事情,但是99%的时间你只想要一个标准的基于设备的RGB color space,所以我们简单的使用了CGColorSpaceCreateDeviceRGB函数去获取需要的引用。
接下来,我们创建了一个数组去记录渐变区域的每一种颜色。0数值可以表示渐变的开始,1表示渐变的结束。我们只有两种颜色,然后我们想用第一种颜色作为开始,第二种颜色作为结束,所以传入了0和1数组。
注意到你可以在颜色渐变中有三种甚至多种你想要的颜色,还可以设定哪种颜色会在渐变这里开始。这在一些效果中将会很有用。之后,我们用传入函数中的颜色去创建一个数组。在这里为了方便,我们使用了普通的NSArray。
我们接着用CGGradientCreateWithColors函数创建了渐变效果,传入了颜色空间,颜色数组,还有之前定义的位置信息。注意到我们必须转换NSArray为一个CFArrayRef – 这相当简单,我们可以用casting方式去做。
起作用的原因是因为NSArray是CGArrayRef的“toll-free bridged” – 基本上是一种奇特的称呼方式,Apple写了所有魔法般的代码去让转换像casting那样简单。
现在我们有一个渐变 的引用了,但是它还没有绘制出任何东西 – 它只是一个指向我们稍后用来绘制它的信息的指针而已。现在我们开始动手吧!在drawLinearGradient方法的“More coming”注释后面添加以下代码:
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
|
首先我们要计算出我们要绘制的渐变效果的开始和结束点。我们从矩形的”顶部中间”到“底部中间”设置一条线。注意到这里使用了来自CGGeometry.h的一些辅助函数(比如 CGRectGetMidX)去计算这些数据(可以让我们的代码更简洁!)。
剩下的代码帮助我们去在提供的矩形中绘制渐变 效果- 关键的函数是 CGContextDrawLinearGradient。关于这个函数比较诡异的一点是,它用渐变填充了整个绘制区域。没有办法让渐变只填充在部分区域中。
好的…没有了裁剪,就是这样!裁剪是Core Graphics的一项出色的功能特性,让你可以在任意形状中限制绘制操作。你需要做的就是添加形状到context上面,然后调用CGContextClip方法,而不是像之前那样填充它。以后的绘制动作都会被限定在那个区域中!
这就是我们在这里要做的。我们把矩形添加到context上面,裁剪它,然后调用CGContextDrawLinearGradient方法,传入之前设定好的所有变量值。
CGContextSaveCGState/CGContextRestoreCGState是什么呢?Core Graphics是一个状态机,一旦你设定了一些操作,需要你修改它才能改变状态。
好的,我们只是裁剪了一个区域,除非我们对裁剪区域做了修改,不然我们都不会在该区域范围之外绘制了!
这就是 CGContextSaveCGState/CGContextRestoreCGState的用处。使用它,我们可以保存当前的context设置到栈中,然后当我们完成操作,回到之前的状态时,让它出栈即可。
最后需要做的 – 我们需要调用 CGGradientRelease方法去清空CGGradientCreateWithColor方法之前创建的内存空间(还有CGColorSpaceRelease方法,感谢@Jim!)。
就是这样!让我们在cell的背景中用上这个函数吧。打开CustomCellBackground.m文件,根据以下内容做修改:
|
编译运行工程,你将看到以下画面:
wow,简单的颜色渐变做出来的效果真不错!
绘制轨迹
到现在为止,table view看起来挺不错的了,但是我们还是要继续做些修改,让它稍微“突出”一点。我们会在边界周围绘制一个白色的矩形,还有cell之间的灰色分隔线。
我们已经知道如何去给矩形填充颜色了 – 同样的,在方框周围绘制线条同样简单!
根据以下代码,对CustomCellBackground.m文件进行修改:
|
我们将要用红色的线去绘制矩形,并把它放置在cell的中间,先让它容易被看到。我们创建一种颜色,然后使用CGRectInset函数稍微缩小方框的尺寸。
CGRectInset方法要做的就是从方框的宽和高减少一定值,然后返回结果。
我们再把绘制颜色设置为红色,设置线的宽度为一像素宽,然后调用CGContextStrokeRect方法去绘制矩形。
编译运行工程,你将看到以下画面:
看起来似乎还OK… 但是否会觉得有点模糊和奇怪?如果你放大它,你将看到一些古怪的现象:
我们用1像素点去绘制(与iPhone3GS的1像素点一样),但是事实上它却用几个像素点去绘制… 怎么会这样子?
像素点线和像素边界线
当你使用Core Graphics去绘制路径时,它会刚好在轨迹边的中间绘制。
我们的情况是,轨迹的边是我们想要去填充的矩形。所以当我们沿着边绘制1像素点的线时,有一半的线(1/2像素)会在矩形的内部,另一半线(1/2像素)会在矩形的外部。
当然,因为没有办法去绘制1/2一个像素,Core Graphics使用了图像保真的方法把两个像素点吸到一起,但是有一个较淡的阴影让它的外表看起来像只绘制了一个像素点。
但是我们不想要图像保真,我们只想要一个像素点!这里有几种方式去修复它:
- 你可以使用裁剪去裁掉不想要的像素
- 你可以取消图像保真并且修改矩形的边界,确保是你想要绘制的线条。
- 你可以修改轨迹去绘制,这样1/2像素的效果就可以考虑了
在本篇教程中,我们会用option #3,修改矩形,让它具有笔画的行为。我们创建一个辅助函数去修改一个矩形为1像素点笔画。
打开Common.h文件,然后添加以下声明到文件的底部:
|
添加以下代码到Common.m文件中:
|
这里我们修改了矩形,让一半边界进入到原来矩形的像素点中,让笔画符合预期效果。
在 CustomCellBackground.m文件中调用以下代码:
|
很好。现在让我们用正确的颜色和位置去完善它。使用以下代码对 CustomCellBackground.m文件进行修改:
|
这里我们减小了1像素点的页面框高度,以便有空间可以放置分隔线,转换它,使用白色去笔画绘制。
编译运行工程,现在应该有一条微小的白色边界在cell的周围。
接下来,我们要在cell之间添加浅灰色的分隔线!
绘制线条
因为我们要在工程中绘制多条线,让我们创建一个辅助函数吧。添加以下代码到Common.h文件中:
|
把以下代码添加到Common.m文件中:
|
好的,我们看下它的原理。在开始和结尾,我们保存和恢复了context,这样我们就不会留下任何的更改操作。
然后设定线条的线帽。默认的设定是让一条线有一个“圆”末端,表示线条刚好在最后的点处结束。
但是这个还不够好,因为我们要让线以1/2点的长度从开始和结束位置缩进,来修正笔画问题。所以我们让线帽有一个“正方形”的末端,表示线条在末端伸长了1/2的线宽 – 就我们的1/2点情况而言 – 太完美了!
然后按照通常那样设定颜色和线条宽度。
接着我们做线条的实际绘制。在Core Graphics中绘制线条,你首先要移动到点A(还没有绘制任何东西),然后添加一条线到点B(在context中从点A添加一条线到点B)。你可以调用CGContextStrokePath方法去绘制线条。
就这样!让我们用它来绘制分隔线,添加以下代码到 CustomCellBackground.m文件的drawRect方法中:
|
编译运行工程,现在你在cell之间应该可以看到漂亮的分隔线了!
现在可以做什么?
这个是上面的工程项目源代码,你可以在这里下载。
到现在你应该对Core Graphics又酷又强大的技术相当熟悉了 – 填充和绘制矩形, 绘制线条和渐变效果,还有裁剪轨迹!我们的table view看起来也挺酷的。
还有更多内容!我们还没讲解如何添加阴影效果,或者弧线,光泽效果,还有其他很酷的技术 – 在下一篇教程中,我们将会添加一个很酷的header到table view上!
同时,如果你有任何的问题,建议或者评论,请提出来!:]