Quarz 2D是一个二维绘画引擎,同时支持ios和mac,其API是Core Graphics框架的,是纯C语言的。IOS系统提供的大部分控件的内容都是通过Quartz 2D画出来的,因此Quartz 2D的一个很重要的价值是:自定义view(自定义UI控件)。
图形上下文(Graphics context)是一个CGContextRef数据,其作用是:
在view中实现- (void)drawRect:(CGRect)rect方法,然后在方法中:
1. 取得跟当前view相关联的图形上下文;
2. 绘制相应的图形内容
3. 利用图形上下文将绘制好的图形内容渲染显示到view上面
//绘制步骤:1、获取当前视图的图形上下文 2、开始绘图 3、渲染绘制内容
/**绘制直线**/
//1、获取当前视图的图形上下文(图形上下文决定绘制的输出目标)
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2、开始绘图
//设置起点
CGContextMoveToPoint(ctx, 20, 50);
//设置终点
CGContextAddLineToPoint(ctx, 300, 50);
//设置线条属性
//设置颜色
CGContextSetStrokeColorWithColor(ctx, [UIColor purpleColor].CGColor);
//另外一种设置颜色的方式
// [[UIColor purpleColor] set];
//设置线条宽度
CGContextSetLineWidth(ctx, 10);
//设置线条起点和终点的样式为圆角
CGContextSetLineCap(ctx, kCGLineCapRound);
//3、将画布上绘制的内容渲染到view的layer上
CGContextStrokePath(ctx);
/**绘制三角形**/
//设置起点
CGContextMoveToPoint(ctx, 150, 80);
//设置第一个拐点
CGContextAddLineToPoint(ctx, 220, 150);
//设置第二个拐点
CGContextAddLineToPoint(ctx, 80, 150);
//设置第三个点(终点)
// CGContextAddLineToPoint(ctx, 150, 80);
//可以用下面方法代替 缝合起点和终点
CGContextClosePath(ctx);
//设置线条的拐点转角样式为圆角
CGContextSetLineJoin(ctx, kCGLineJoinRound);
//渲染
CGContextStrokePath(ctx);
/**绘制空心四边形**/
CGContextAddRect(ctx, CGRectMake(40, 200, 200, 100));
//设置空心(线条)颜色
// CGContextSetStrokeColorWithColor(ctx, [UIColor lightGrayColor].CGColor);
//也可以这样设置颜色
[[UIColor lightGrayColor] setStroke];
//设置线条宽度
CGContextSetLineWidth(ctx, 10);
//渲染(空心的)
CGContextStrokePath(ctx);
/**绘制实心四边形**/
CGContextAddRect(ctx, CGRectMake(40, 320, 200, 100));
//设置实心颜色
// CGContextSetFillColorWithColor(ctx, [UIColor orangeColor].CGColor);
[[UIColor orangeColor] setFill];
//渲染(实心的)
CGContextFillPath(ctx);
/**绘制圆(可以用绘制椭圆的方式画圆)**/
//参数依次为圆心x,圆心y,半径,开始位置的弧度,结束位置的弧度,绘制路径(1为顺时针,0为逆时针)
//由于Quartz2D的坐标系是x轴向右,y轴向上,不同于UIKit坐标系。因此在不将Quartz2D坐标系翻转的情况下,画出来的图形是与原图形关于x轴对称的。
CGContextAddArc(ctx, 100, 520, 50, 0, M_PI_2, 1);
CGContextStrokePath(ctx); //空心
// CGContextFillPath(ctx); //实心
/**绘制椭圆**/
CGContextAddEllipseInRect(ctx, CGRectMake(230, 400, 120, 200));
CGContextSetStrokeColorWithColor(ctx, [UIColor blueColor].CGColor);
CGContextStrokePath(ctx);
/**绘制弧线**/
// CGContextAddArcToPoint(ctx, 100, 200, 100, 200, 50);
CGContextAddArc(ctx, 200, 200, 100, 0, M_PI, 1);
CGContextSetStrokeColorWithColor(ctx, [UIColor cyanColor].CGColor);
CGContextStrokePath(ct
举个例子,假如要绘制两条线,一条红色一条默认的黑色。先绘制红色线,绘制完毕渲染上去后,再去绘制第二条。绘制第二条的时候如果不重新设置绘画颜色,那么绘制出来的线条也是红色的。也就是说,绘制属性如果不对其清空(即重新设置)是默认保留在图形上下文上的。因此可以这样理解:
一个图形上下文有3块区域,分别是绘制属性,图像信息,绘制区域:
前面说过如果要绘制多个不同属性的图形,那么每次渲染好一个图形后就要重新设置绘制属性,通常绘制多个图形都是这样做的。有时候可能用到一个简单的方法:即在绘制一个图形前先保存当前图形上下文中的绘制属性,这个绘制属性会被保存到图形上下文栈上,如果下次需要绘制同样属性的图形,直接把这个绘制属性从栈顶取出来(恢复)就好了。需要注意的是保存一次只能取一次,可以保存多次,但是每次只从栈顶取。
/**保存绘制属性(以绘制3条线为例,第一条第三条属性一致)**/
//第一条
CGContextMoveToPoint(ctx, 20, 300);
CGContextAddLineToPoint(ctx, 200,300);
//设置绘制属性
CGContextSetLineWidth(ctx, 10);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetStrokeColorWithColor(ctx, [UIColor orangeColor].CGColor);
CGContextStrokePath(ctx);
//第二条线
//先保存当前的绘制属性
CGContextSaveGState(ctx);
CGContextMoveToPoint(ctx, 20, 400);
CGContextAddLineToPoint(ctx, 200, 400);
//设置新的绘制属性
CGContextSetLineWidth(ctx, 5);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextSetStrokeColorWithColor(ctx, [UIColor blueColor].CGColor);
CGContextStrokePath(ctx);
//第三条线
//取出(恢复)之前保存的绘制属性
CGContextRestoreGState(ctx);
CGContextMoveToPoint(ctx, 20, 500);
CGContextAddLineToPoint(ctx, 200, 500);
CGContextStrokePath(ctx);
矩阵操作主要有旋转操作、缩放操作、平移操作,是以视图左上角为原点进行的。对矩阵的操作一定要在绘制之前完成,不然绘制完了再操作无效。
CGContextRef ctx = UIGraphicsGetCurrentContext();
//矩阵旋转45度(参数为图形上下文、旋转角度)是以左上角为旋转点的
//设置矩阵操作要在绘制前完成
// CGContextRotateCTM(ctx, M_PI_4);
//缩放(参数为图形上下文,x方向缩放比例,y方向缩放比例)
// CGContextScaleCTM(ctx, 0.5, 0.5);
//平移(参数为图形上下文,x方向平移距离,y方向平移距离)
CGContextTranslateCTM(ctx, 100, 100);
CGContextAddRect(ctx, CGRectMake(100, 100, 100, 100));
//标记
NSString *loc1 = @"1";
NSString *loc2 = @"2";
[loc1 drawAtPoint:CGPointMake(99, 99) withAttributes:nil];
[loc2 drawAtPoint:CGPointMake(201, 99) withAttributes:nil];
CGContextStrokePath(ctx);
指剪切掉指定区域意外的部分,只保留该区域内的内容。
原则:先设置好剪切区域,或者说剪切方法,再去绘制相关内容。
CGContextRef ctx = UIGraphicsGetCurrentContext();
//剪切自定义区域意外的部分(以剪切方法为三角形,剪切内容为图片为例)
// // CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 50, 50));
// CGContextMoveToPoint(ctx, 100, 100);
// CGContextAddLineToPoint(ctx, 60, 150);
// CGContextAddLineToPoint(ctx, 140, 150);
// CGContextClosePath(ctx);
// CGContextClip(ctx);
//剪切指定矩形区域意外的部分
CGContextClipToRect(ctx, CGRectMake(80, 100, 10, 10));
UIImage *image = [UIImage imageNamed:@"google"];
[image drawAtPoint:CGPointMake(80, 100)];
我们之前画一条直线,都是直接设置好它的起点和终点,然后就开始画了。画一个圆,设置好圆心半径起点终点和方向即可。事实上,我们设置好这些绘图信息后,系统会默认创建一条绘图路径,画图就是根据这条路径来画的。一条线对应一条路径,一个圆对应另一条路径。那么我们自然可以通过手动创建路径的方式绘制,需要绘制几个图案,就要创建几条路径。
A、Quartz2D中所有通过creat/copy/retain方法创建出来的值都要释放,以path为例:
B、可以将要绘制的所有路径加入到图形上下文后,最后一次性渲染。
//绘制一条直线的两种方法(两种方式是等效的)
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(ctx, 20, 200);
CGContextAddLineToPoint(ctx, 300, 200);
CGContextStrokePath(ctx);
//手动创建路径绘制
//创建一条路径
CGMutablePathRef path = CGPathCreateMutable();
//添加绘图信息到路径
CGPathMoveToPoint(path, NULL, 20, 300);
CGPathAddLineToPoint(path, NULL, 300, 300);
//将路径添加到图形上下文
CGContextAddPath(ctx, path);
//创建另一条路径
CGMutablePathRef path2 = CGPathCreateMutable();
CGPathAddEllipseInRect(path2, NULL, CGRectMake(100, 400, 100, 100));
CGContextAddPath(ctx, path2);
CGContextStrokePath(ctx);
//渲染所有路径对应的图案
CGContextStrokePath(ctx);
//Quartz2D中所有通过creat/copy/retain方法创建出来的值都要释放
CGPathRelease(path);
CGPathRelease(path2);
//或者
// CFRelease(path);
Quartz2D提供了以下几种类型的图形上下文
常用的是Bitmap Graphics Context。所谓Bitmap,其实就是UIImage,这也是最常用到的图形上下文,通常用它来生成一张图片。步骤如下:
创建一个bitmap图形上下文(有两种方式)
A.UIGraphicsBeginImageContext(<#CGSize size#>);
B.UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale);
这两种方法都可以创建一个bitmap图形上下文,但是第一种创建的图片清晰度和质量没有第二种好。方法二接收的参数依次为:
获取创建好的bitmap图形上下文然后在上面做文章(画图)
//点击按钮截屏
- (void)actionShot:(UIButton *)sender{
//可以隐藏按钮,渲染完后显示回来
self.buttonShot.hidden = YES;
//创建图形上下文
UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.view.frame.size.width, self.view.frame.size.height), NO, 0);
//获取图形上下文并将当前屏幕渲染到图形上下文上
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
[delegate.window .layer renderInContext:UIGraphicsGetCurrentContext()];
//从图形上下文中取出绘制好的图片
UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
//关闭图形上下文
UIGraphicsEndImageContext();
self.buttonShot.hidden = NO;
// //截屏完毕 有时候可能想获取屏幕中指定区域的图片,如下操作
// //得到截屏的cgimage
CGImageRef image = screenImage.CGImage;
//设置目标区域,注意这里需要考虑retina分辨率的放大倍数,以iphone6plus为例,在原尺寸的基础上*3,这里就不判断了。
CGRect rect = CGRectMake(0, 0, screenImage.size.width*3, screenImage.size.height*3);
//取出目标区域的图片
CGImageRef targetImage = CGImageCreateWithImageInRect(image, rect);
//最终图片
UIImage *finalImage = [UIImage imageWithCGImage:targetImage];
//保存到相册
UIImageWriteToSavedPhotosAlbum(finalImage, self, @selector(image: didFinishSavingWithError:contextInfo:), nil);
//保存到沙盒
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString *currentTime = [dateFormatter stringFromDate:[NSDate date]];
NSString *imagePath = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"ScreenShot_%@.png",currentTime]];
NSData *imageDate = UIImagePNGRepresentation(finalImage);
[imageDate writeToFile:imagePath atomically:YES];
CGImageRelease(targetImage);
}
//保存至相册后的回调
- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo
{
NSString *msg = nil ;
if(error != NULL){
msg = @"保存图片失败" ;
}else{
msg = @"保存图片成功" ;
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"保存图片结果提示"
message:msg
delegate:self
cancelButtonTitle:@"确定"
otherButtonTitles:nil];
[alert show];
}
前面讲绘图路径的时候提到过内存管理,下面再总结一下:
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/lotheve/article/details/47622343