【IOS 开发学习总结-OC-65】Quartz 2D绘图(4-2)——绘制文本+设置阴影+使用路径
2015-10-23 16:39
573 查看
【IOS 开发学习总结-OC-65】Quartz 2D绘图(中)——绘制文本+设置阴影+使用路径
绘制文本
CGContextRef为绘制文字提供了如下函数。CGAffineTransform CGContextGetTextMatrix(CGContextRef c):获取当前对文本执行变换的变换矩阵。
CGPoint CGContextGetTextPosition(CGContextRef c):获取该CGContextRef中当前绘制文本的位置。
CGContextSelectFont(CGContextRef c, const char *name, CGFloat size, CGTextEncoding textEncoding):设置该CGContextRef当前绘制文本的字体、字体大小。
void CGContextSetCharacterSpacing(CGContextRef c, CGFloat spacing):设置该CGContextRef中绘制文本的字符间距。
void CGContextSetFont(CGContextRef c, CGFontRef font):设置该CGContextRef中绘制文本的字体。
void CGContextSetFontSize(CGContextRef c, CGFloat size):设置该CGContextRef中绘制文本的字体大小。
CGContextSetTextDrawingMode(CGContextRef c, CGTextDrawingMode mode):设置该CGContextRef绘制文本的绘制模式。该函数支持kCGTextFill、kCGTextStroke、kCGTextFillStroke等绘制模式。
void CGContextSetTextMatrix(CGContextRef c, CGAffineTransform t):设置对将要绘制的文本执行指定的变换。
CGContextSetTextPosition(CGContextRef c , CGFloat x, CGFloat y):设置CGContextRef的一个文本的绘制位置。
void CGContextShowText(CGContextRef c,
const char *string,
size_t length):控制CGContextRef在当前绘制点绘制指定文本。
void CGContextShowTextAtPoint (CGContextRef c,
CGFloat x,
CGFloat y,
const char *string,
size_t length):控制CGContextRef在指定绘制点绘制指定文本。
除此之外,系统还为NSString提供了一个UIStringDrawing分类,通过该分类为NSString增加了drawAtPoint:withAttributes:、drawInRect:withAttributes:等绘制方法,通过这些绘制方法可以更方便地绘制字符串。
总结起来,使用CGContextRef绘制文本的步骤如下。
获取绘图的CGContextRef。
设置绘制文本的相关属性,例如,绘制文本所用的绘制方式、字体大小、字体名称等。
如果只是绘制不需要进行变换的文本,直接调用NSString的drawAtPoint:withAttributes:、drawInAttributes:withFont:等方法绘制即可。如果需要对绘制的文本进行变换,则需要先调用CGContextSetTextMatrix()函数设置变换矩阵,再调用CGContextShowTextAtPoint()方法绘制文本。
下面的程序示范了如何利用Quartz 2D绘制文本。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。将该界面设计文件中最大的View改为使用自定义的FKTextView类,并向该界面设计文件添加两个UISlider控件。为了让这两个UISlider控件控制文本的缩放和旋转角度,将控制缩放的UISlider的最小值、最大值分别设置为0.1、5;将控制旋转的UISlikder的最小值、最大值设分别为-90、90,程序为两个UISlider的Value Changed事件分别绑定scaleChanged:和rotateChanged: IBActino方法。
下面是该应用的视图控制器类的实现代码。
程序清单:codes/12/12.2/DrawTextTest/DrawTextTest/FKViewController.m
@implementation FKViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; } - (IBAction)scaleChanged:(id)sender { // 修改FKTextView的scaleRate属性 ((FKTextView*)self.view).scaleRate = ((UISlider*)sender).value; } - (IBAction)rotateChanged:(id)sender { // 修改FKTextView的rotateAngle属性 ((FKTextView*)self.view).rotateAngle = ((UISlider*)sender).value; } @end
从上面的控制器代码可以看出,当用户拖动界面的UISlider时,视图控制器中的监听器方法就会改变FKTextView控件的scaleRate、rotateAngle属性。当这些属性被修改时,FKTextView就会使用特定的缩放比、旋转角重绘文本。下面是FKTextView类的实现代码。
程序清单:codes/12/12.2/DrawTextTest/DrawTextTest/FKTextView.m
@implementation FKTextView - (void)setScaleRate:(CGFloat)scaleRate { if(_scaleRate != scaleRate) { _scaleRate = scaleRate; [self setNeedsDisplay]; // 通知该控件重绘自己 } } - (void)setRotateAngle:(CGFloat)rotateAngle { if(_rotateAngle != rotateAngle) { _rotateAngle = rotateAngle; [self setNeedsDisplay]; // 通知该控件重绘自己 } } // 重写该方法绘制该控件 - (void)drawRect:(CGRect)rect { CGContextRef ctx = UIGraphicsGetCurrentContext(); // 获取该控件的绘图CGContextRef CGContextSetCharacterSpacing (ctx, 4); // 设置字符间距 CGContextSetRGBFillColor (ctx, 1, 0, 1, 1); // 设置填充颜色 CGContextSetRGBStrokeColor (ctx, 0, 0, 1, 1); // 设置线条颜色 CGContextSetTextDrawingMode (ctx, kCGTextFill); // 设置使用填充模式绘制文字 // 绘制文字 [@"疯狂iOS讲义" drawAtPoint:CGPointMake(10 ,20) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIFont fontWithName:@"Arial Rounded MT Bold" size: 45], NSFontAttributeName, [UIColor magentaColor] , NSForegroundColorAttributeName , nil]]; // 设置使用描边模式绘制文字 CGContextSetTextDrawingMode (ctx, kCGTextStroke); // 绘制文字 [@"疯狂Android讲义" drawAtPoint:CGPointMake(10 ,80) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIFont fontWithName:@"Heiti SC" size: 40],NSFontAttributeName, [UIColor blueColor] , NSForegroundColorAttributeName , nil]]; // 设置使用填充、描边模式绘制文字 CGContextSetTextDrawingMode (ctx, kCGTextFillStroke); // 绘制文字 [@"疯狂Ajax讲义" drawAtPoint:CGPointMake(10 ,130) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIFont fontWithName:@"Heiti SC" size: 50], NSFontAttributeName, [UIColor magentaColor] , NSForegroundColorAttributeName , nil]]; // 定义一个垂直镜像的变换矩阵 CGAffineTransform yRevert = CGAffineTransformMake(1, 0, 0, -1, 0, 0); // 设置绘制文本的字体和字体大小 CGContextSelectFont (ctx, "Courier New" , 40, kCGEncodingMacRoman); // 为yRevert变换矩阵根据scaleRate添加缩放变换矩阵 CGAffineTransform scale = CGAffineTransformScale(yRevert, self.scaleRate, self. scaleRate); // 为scale变换矩阵根据rotateAngle添加旋转变换矩阵 CGAffineTransform rotate = CGAffineTransformRotate(scale, M_PI * self.rotateAngle / 180); CGContextSetTextMatrix(ctx, rotate); // 对CGContextRef绘制文字时应用变换 CGContextShowTextAtPoint(ctx, 50, 300, "crazyit.org", 11); // 绘制文本 } @end
上面的程序中,前三行粗体字代码直接调用了NSString的drawAtPoint:withAttributes:方法进行绘制–这是一种非常简单的绘制方式。
接下来需要对绘制的文本执行坐标变换,因此程序中先用CGAffineTransformMake()函数定义了一个变换矩阵:(1, 0, 0, -1, 0, 0),该变换矩阵将会对绘制的文本做垂直镜像。此处这么做的原因是因为Quartz 2D本身的坐标系统与UIView控件绘图的坐标系统并不相同,Quartz 2D为了照顾开发者的习惯,已经对坐标系统做过变换,但它在处理文本变换时少了一次垂直镜像变换,导致绘制出来的文本总是上下颠倒的。为了解决这个问题,笔者先对文本做了一次垂直镜像变换。
关于坐标变换的详细理论,后面会详细阐述,读者可参考相关内容。
然后利用CGAffineTransformScale()、CGAffineTransformRotate()增加了缩放、旋转变换,程序最后一行粗体字代码调用CGContextShowTextAtPoint()方法绘制了文本,该文本将会带有缩放和旋转效果。
上面的程序中,在绘制文本时,使用了大量iOS支持的字体,如果读者需要获取iOS支持的所有字体,可调用UIFont类的familyNames类方法,该方法返回一个数组,该数组包含iOS所支持的全部字体。
编译、运行该程序,用户即可通过拖动界面上的UISlider控件来改变文本的缩放、旋转,效果如图12.5所示。
设置阴影
前面已经介绍了CGContextRef为设置图形阴影提供了如下两个函数。void CGContextSetShadow(CGContextRef context, CGSize offset, CGFloat blur):该函数设置阴影在X、Y方向上的偏移,并设置阴影的模糊程度。该函数的offset包含两个CGFloat值,第1个CGFloat值控制阴影在X方向的偏移,如果该值为正,则向右偏移,否则向左偏移;第2个CGFloat值控制阴影在Y方向的偏移,如果该值为正,则向下偏移,否则向上偏移。最后一个blur参数控制阴影的模糊程度,如果blur参数为1,表明阴影几乎不模糊,blur参数越大,阴影越模糊。
void CGContextSetShadowWithColor(CGContextRef context, CGSize offset, CGFloat blur, CGColorRef color):该函数与前一个函数的功能基本相似,只是该函数多了一个属性用于设置阴影颜色。
下面的程序示范了为所绘制的形状添加阴影。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。将该界面设计文件中最大的View改为使用自定义的FKShadowView类。该程序的控制器类几乎无须修改,只要重写FKShadowView的drawRect:方法,在该方法中设置阴影,并绘制带阴影的形状和文本即可。下面是FKShadowView类的实现代码。
程序清单:codes/12/12.2/ShadowTest/ShadowTest/FKShadowView.m
@implementation FKShadowView - (void)drawRect:(CGRect)rect { CGContextRef ctx = UIGraphicsGetCurrentContext(); // 获取绘图的CGContextRef // 使用默认的阴影颜色,阴影向左上角投影,模糊度为5 CGContextSetShadow(ctx, CGSizeMake(8, -6), 5); CGContextSetRGBFillColor (ctx, 1, 0, 1, 1); // 设置填充颜色 CGContextSetRGBStrokeColor (ctx, 0, 0, 1, 1); // 设置线条颜色 CGContextSetTextDrawingMode (ctx, kCGTextFill); // 设置使用填充模式绘制文字 // 绘制文字 [@"疯狂iOS讲义" drawAtPoint:CGPointMake(10 ,20) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIFont fontWithName:@"Arial Rounded MT Bold" size: 45] , NSFontAttributeName , [UIColor magentaColor] , NSForegroundColorAttributeName, nil]]; // 设置使用描边模式绘制文字 CGContextSetTextDrawingMode (ctx, kCGTextStroke); // 绘制文字 [@"疯狂Android讲义" drawAtPoint:CGPointMake(10 ,80) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIFont fontWithName:@"Heiti SC" size: 40], NSFontAttributeName , [UIColor magentaColor] , NSForegroundColorAttributeName, nil]]; // 使用默认的阴影颜色,阴影向右下角投影,模糊度为20 CGContextSetShadowWithColor(ctx, CGSizeMake(10, 8), 10 , [[UIColor redColor] CGColor]); CGContextFillRect(ctx, CGRectMake(20 , 150 , 180 , 80)); CGContextSetRGBStrokeColor (ctx, 1, 0, 1, 1); // 设置线条颜色 CGContextStrokeRect(ctx, CGRectMake(30 , 260 , 180 , 80)); }
上面程序中的第一行粗体字代码使用默认的阴影颜色,通过传入CGSizeMake(8, -6)参数控制阴影向左上角投影;第二行粗体字代码使用红色作为阴影颜色,通过传入CGSizeMake(10, 8)参数控制阴影向右下角投影。编译、运行该程序,即可看到如图12.6所示的效果。
使用路径
正如前面提到的,Quartz 2D只提供了绘制矩形、椭圆的函数,并没有直接提供绘制圆角矩形、三角形等几何图形的函数。为了绘制更复杂的图形,必须启用路径。表12.4列出了Quartz 2D为创建路径提供的函数。
表12.4 创建路径的相关函数
除此之外,Quartz 2D还提供了如下函数来获取当前CGContextRef所包含的路径信息。
bool CGContextIsPathEmpty(CGContextRef c):该函数用于判断指定CGContextRef包含的路径是否为空。
CGPoint CGContextGetPathCurrentPoint(CGContextRef c):该函数用于返回指定CGContextRef包含的路径的当前点。
CGRect CGContextGetPathBoundingBox(CGContextRef c):该函数用于返回指定CGContextRef中能完整包围所有路径的最小矩形。
bool CGContextPathContainsPoint(CGContextRef context, CGPoint point, CGPathDrawingMode mode):该函数判断指定CGContextRef包含的路径按指定绘制模式进行绘制时,是否需要绘制point点。
为了在Canvas中使用路径,可按如下步骤进行。
调用CGContextBeginPath()函数开始定义路径。
调用表12.4所示的各种函数添加子路径。
如果路径添加完成,调用CGContextClosePath()函数关闭路径。
调用CGContextDrawPath()、CGContextEOFillPath()、CGContextFillPath()或CGContextStrokePath()函数来填充路径或绘制路径边框即可。在这些方法中,第一个方法可以代替后面的几个方法,第一个方法指定使用特定的模式来绘制图形,它支持如下几种绘制方式。
kCGPathFill:指定填充路径。相当于CGContextFillPath()函数。
kCGPathEOFill:指定采用even-odd模式填充路径。相当于CGContextEOFillPath()函数。
kCGPathStroke:指定只绘制路径。相当于CGContextStrokePath()函数。
kCGPathFillStroke:指定既绘制路径,也填充路径。如果采用这种绘制方式,必须使用该函数。
kCGPathEOFillStroke:指定既绘制路径,也采用even-odd模式填充路径。如果采用这种绘制方式,必须使用该函数。
通过上面介绍的CGContextAddArc()函数,我们可以绘制扇形,下面的程序代码使用循环绘制了10个扇形,而且这10个扇形的透明度逐渐降低。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。将该界面设计文件中最大的View改为使用自定义的FKArcView类。该程序的控制器类几乎无须修改,只要重写FKArcView的drawRect:方法,在该方法中使用路径绘制扇形即可。
下面是FKArcView类的实现代码。
程序清单:codes/12/12.2/AddArcTest/AddArcTest/FKArcView.m
@implementation FKArcView - (void)drawRect:(CGRect)rect { CGContextRef ctx = UIGraphicsGetCurrentContext(); for(int i = 0 ; i < 10 ; i++) { CGContextBeginPath(ctx); // 开始定义路径 // 添加一段圆弧,最后一个参数1代表逆时针,0代表顺时针 CGContextAddArc(ctx , i * 25 , i * 25, (i + 1) * 8 , M_PI * 1.5 , M_PI , 0); CGContextClosePath(ctx); // 关闭路径 CGContextSetRGBFillColor(ctx, 1, 0, 1, (10 - i) * 0.1); // 设置填充颜色 CGContextFillPath(ctx); // 填充当前路径 } } @end
上面程序中的粗体字代码绘制了从M_PI * 1.5角度(12点方向)开始,到M_PI角度(9点方向)结束的扇形–在该绘图API中0度代表X轴方向(也就是3点方向)。需要指出的是,关于CGContextAddArc()函数的最后一个参数,官方文档介绍该参数为0代表逆时针,但笔者运行时看到的效果是0代表顺指针(可参见下面的运行效果)。
编译、运行该程序,可以看到如图12.7所示的效果。
从上面程序的运行结果来看,相信读者对CGContextAddArc (CGContextRef c, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)函数的用法已经比较清楚,该方法的第2、3个参数指定圆弧的圆心,第4个参数用于设置圆弧的半径,第5、6个参数则用于设置圆弧的开始角度、结束角度,最后一个参数用于设置是否顺时针旋转。
可能有读者对CGContextAddArcToPoint(CGContextRef c, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat radius)函数感到疑惑,该方法也是添加一段圆弧路径。确定这段圆弧的方式是:假设从当前点到P1(x1、y1)绘制一条线条,再从 P1(x1、y1)到P2(x2、y2)绘制一条线条,CGContextAddArcToPoint()则绘制一条同时与上面两条线条相切且半径为radius的圆弧。该函数方法的示意图如图12.8所示。
借助CGContextAddArcToPoint()和CGContextAddLineToPoint()函数可以非常容易地实现添加圆角矩形路径的方法(所谓圆角矩形,就是在矩形的每个角都绘制一段90°的圆弧)。除此之外,还有CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)函数用于把绘制点移动到指定位置,借助这些方法还可以绘制任意的多角星。
下面的程序代码将会开发两个自定义函数,一个用于添加圆角矩形,一个用于添加多角星。程序代码如下。
程序清单:codes/12/12.2/PathTest/FKContext.h
#ifndef PathTest_FKContext_h #define PathTest_FKContext_h /* 该方法负责绘制圆角矩形。x1、y2:是圆角矩形左上角的座标;width、height:控制圆角矩行的宽、高; radius:控制圆角矩形的四个圆角的半径 */ void CGContextAddRoundRect(CGContextRef c, CGFloat x1 , CGFloat y1 , CGFloat width , CGFloat height , CGFloat radius) { CGContextMoveToPoint (c, x1 + radius , y1); // 移动到左上角 CGContextAddLineToPoint(c , x1 + width - radius, y1); // 添加一条连接到右上角的线段 // 添加一段圆弧 CGContextAddArcToPoint(c , x1 + width , y1, x1 + width, y1 + radius, radius); // 添加一条连接到右下角的线段 CGContextAddLineToPoint(c , x1 + width, y1 + height - radius); // 添加一段圆弧 CGContextAddArcToPoint(c , x1 + width, y1 + height , x1 + width - radius , y1 + height , radius); // 添加一条连接到左下角的线段 CGContextAddLineToPoint(c , x1 + radius, y1 + height); // 添加一段圆弧 CGContextAddArcToPoint(c , x1, y1 + height , x1, y1 + height - radius , radius); // 添加一条连接到左上角的线段 CGContextAddLineToPoint(c , x1 , y1 + radius); // 添加一段圆弧 CGContextAddArcToPoint(c , x1 , y1 , x1 + radius , y1 , radius); } /* 该方法负责绘制多角星。n:该参数通常应设为奇数,控制绘制N角星。 dx、dy:控制N角星的中心。size:控制N角星的大小 */ void CGContextAddStar(CGContextRef c , NSInteger n, CGFloat dx , CGFloat dy , NSInteger size) { CGFloat dig = 4 * M_PI / n ; CGContextMoveToPoint(c , dx , dy + size); // 移动到指定点 for(int i = 1 ; i <= n ; i++) { CGFloat x = sin(i * dig); CGFloat y = cos(i * dig); // 绘制从当前点连接到指定点的线条 CGContextAddLineToPoint(c , x * size + dx ,y * size + dy); } } #endif
上面两个函数都没有执行任何绘制操作,它们都只是向当前CGContextRef中添加路径,第一个函数用于添加一个圆角矩形路径,第二个函数用于添加一个多角星路径。
上面这个函数库与项目无关,该函数库中包含的两个函数完全可以在任何项目中复用。因此,如果读者以后的项目需要绘制圆角矩形、多角星,只要将该文件复制到项目中,并导入该函数库,即可调用这两个函数添加路径。
实例:绘制任意多角星
下面创建一个应用来绘制圆角矩形和多角星。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。将该界面设计文件中最大的View改为使用自定义的FKPathView类。该程序的控制器类几乎无须修改,只要重写FKPathView的drawRect:方法,在该方法中调用FKContext.h中的方法来添加圆角矩形、多角星路径,然后根据需要采用不同的方式绘制这些路径即可。
下面是FKPathView类的实现代码。
程序清单:codes/12/12.2/PathTest/FKPathView.m
#import "FKPathView.h" #import "FKContext.h" @implementation FKPathView - (void)drawRect:(CGRect)rect { CGContextRef ctx = UIGraphicsGetCurrentContext(); // 获取绘图CGContextRef CGContextBeginPath(ctx); // 开始添加路径 CGContextAddStar(ctx, 5, 80, 150, 40); // 添加一个五角星的路径 CGContextAddRoundRect(ctx, 10, 30, 150, 70, 14); // 添加一个圆角矩形的路径 CGContextClosePath(ctx); // 关闭路径 CGContextSetRGBStrokeColor(ctx, 1, 1, 0, 1); // 设置线条颜色 CGContextSetLineWidth(ctx, 4); // 设置线宽 CGContextStrokePath(ctx); // 绘制路径 CGContextBeginPath(ctx); // 开始添加路径 CGContextAddStar(ctx, 5, 240, 150, 40); // 添加一个五角星的路径 CGContextAddRoundRect(ctx, 170, 30, 130, 70, 14); // 添加一个圆角矩形的路径 CGContextClosePath(ctx); // 关闭路径 CGContextSetRGBFillColor(ctx, 1, 0, 1, 1); // 设置填充颜色 CGContextDrawPath(ctx, kCGPathFillStroke); // 采用填充并绘制路径的方式来绘制路径 CGContextBeginPath(ctx); // 开始添加路径 CGContextAddStar(ctx, 3, 60, 220, 40); // 添加一个三角形的路径 CGContextClosePath(ctx); // 关闭路径 CGContextSetRGBFillColor(ctx, 1, 0, 0, 1); // 设置填充颜色 CGContextFillPath(ctx); // 填充路径 CGContextBeginPath(ctx); // 开始添加路径 CGContextAddStar(ctx, 7, 160, 220, 40); // 添加一个7角星的路径 CGContextClosePath(ctx); // 关闭路径 CGContextSetRGBFillColor(ctx, 0, 1, 0, 1); // 设置填充颜色 CGContextFillPath(ctx); // 填充路径 CGContextBeginPath(ctx); // 开始添加路径 CGContextAddStar(ctx, 9, 260, 220, 40); // 添加一个9角星的路径 CGContextClosePath(ctx); // 关闭路径 CGContextSetRGBFillColor(ctx, 0, 0, 1, 1); // 设置填充颜色 CGContextFillPath(ctx); // 填充路径 } @end
上面程序的第一行粗体字代码导入了FKContext.h文件,接下来的粗体字代码多次调用了CGContextAddRoundRect()函数和CGContextAddStar()函数来添加圆角矩形和多角星。编译、运行该程序,即可看到如图12.9所示的效果。
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- Delphi使用OpenGL2d绘图之画图片Bmp的方法
- 讲解iOS开发中基本的定位功能实现
- js判断客户端是iOS还是Android等移动终端的方法
- IOS开发环境windows化攻略
- 检测iOS设备是否越狱的方法
- .net平台推送ios消息的实现方法
- 探讨Android与iOS,我们将何去何从?
- Android、iOS和Windows Phone中的推送技术详解
- java定时调度器(Quartz)使用实例
- quartz实现定时功能实例详解(servlet定时器配置方法)
- IOS 改变键盘颜色代码
- 举例详解iOS开发过程中的沙盒机制与文件
- Android和IOS的浏览器中检测是否安装某个客户端的方法