Quartz2D

作者: iOS_Cqlee | 来源:发表于2015-10-07 11:52 被阅读332次

    Quartz2D

    简介

    Quartz2D是二维(平面)的绘图引擎(经包装的函数库,方便开发者使用。也就是说苹果帮我们封装了一套绘图的函数库)

    同时支持iOS和Mac系统开发,用Quartz2D写的同一份代码,既可以运行在iphone上又可以运行在mac上,可以跨平台开发。

    Quartz 2D能完成的工作

    绘制图形 : 线条\三角形\矩形\圆\弧等

    绘制文字

    绘制\生成图片(图像)

    读取\生成PDF

    截图\裁剪图片

    自定义UI控件

    .....

    Quartz2D在iOS开发中的价值

    为了便于搭建美观的UI界面,iOS提供了UIKit框架,里面有各种各样的UI控件 UILabel:显示文字 UIImageView:显示图片 UIButton:同时显示图片和文字(能点击) … …

    利用UIKit框架提供的控件,拼拼凑凑,能搭建和现实一些简单、常见的UI界面,但是,有些UI界面极其复杂、而且比较个性化,用普通的UI控件无法实现,这时可以利用Quartz2D技术将控件内部的结构画出来,自定义控件的样子,iOS中大部分控件的内容都是通过Quartz2D画出来的,因此,Quartz2D在iOS开发中很重要的一个价值是:自定义view(自定义UI控件)

    自定义View

    图形上下文(Graphics Context):是一个CGContextRef类型的数据

    图形上下文的作用

    保存绘图信息、绘图状态 -决定绘制的输出目标(绘制到什么地方去?) (输出目标可以是PDF文件、Bitmap或者显示器的窗口上) 相同的一套绘图序列,指定不同的Graphics Context,就可将相同的图像绘制到不同的目标上

    自定义view的步骤

    新建一个类,继承自UIView

    实现- (void)drawRect:(CGRect)rect方法,然后在这个方法中

    取得跟当前view相关联的图形上下文

    绘制相应的图形内容

    利用图形上下文将绘制的所有内容渲染显示到view上面

    drawRect:方法

    实现drawRect:方法才能绘图到view,因为在drawRect:方法中才能取得跟view相关联的图形上下文,

    drawRect:方法在调用时间 当view第一次显示到屏幕上时(被加到UIWindow上显示出来) 调用view的setNeedsDisplay或者setNeedsDisplayInRect:时

    在drawRect:方法中取得上下文后,就可以绘制东西到view上

    View内部有个layer(图层)属性,drawRect:方法中取得的是一个Layer Graphics Context,因此,绘制的东西其实是绘制到view的layer上去了,View之所以能显示东西,完全是因为它内部的layer

    示例1画直线

    1> 获取图形上下文 CG:表示这个类在CoreGraphics框架里 Ref:引用 目前学的上下文都跟UIGraphics有关,想获取图形上下文,首先敲UIGraphics。

    2> 拼接路径:一般开发中用贝塞尔路径,里面封装了很多东西,可以帮我画一些基本的线段,矩形,圆等等。 创建贝塞尔路径 起点:moveToPoint 终点:addLineToPoint

    3> 把路径添加到上下文 CGPath转换:UIKit框架转CoreGraphics直接CGPath就能转

    4> 把上下文渲染到视图,图形上下文本身不具备显示功能。 PPT画图分析为什么要这样做?首先获取图形上下文,然后描述路径,把路径添加到上下文,渲染到视图,图形上下文相当于一个内存缓存区,在内存里面操作是最快的,比直接在界面操作快多了。

    在添加一根线 直接addLineToPoint,因为路径是拼接的,默认下一条线的起点是上一条线的终点。

    画两跟不连接的线 1> 第二次画的时候,重新设置起点,然后画线。一个路径可以包含多条线段。 2> 新创建一个路径,添加到上下文。开发中建议使用这种,比较容易控制每根线。

    设置绘图状态 线段怎么加粗。 绘图状态调用顺序:只要在渲染之前就好了,在渲染的时候才会去看绘图的最终状态。

    // 1.获取跟当前view想关联的上下文// 以后只要根上下文有关,直接敲UIGraphics,一般都是以UIGraphics开头// CG:CoreGraphics Ref:引用CGContextRefctx =UIGraphicsGetCurrentContext();// 2.绘制内容,拼接路径,绘制的内容统称为路径// 在开发中一般使用贝塞尔路径,UIKit框架UIBezierPath*path = [UIBezierPathbezierPath];// 设置起点,移动到某个位置[path moveToPoint:CGPointMake(50,50)];// 添加一根线到某个点[path addLineToPoint:CGPointMake(200,200)];// 一根路径对象可以包含很多线段[path moveToPoint:CGPointMake(50,200)];// 默认下一根线的起点在上一根线的终点[path addLineToPoint:CGPointMake(100,200)];// 3.把路径添加到上下文,给上下文添加路径以CGContextCGContextAddPath(ctx, path.CGPath);// 开发中,如果线段不连接,最好使用一根线对应一个路径对象// 描述第二根线//    path = [UIBezierPath bezierPath];////    // 设置起点,移动到某个位置//    [path moveToPoint:CGPointMake(200, 200)];////    // 添加一根线到某个点//    [path addLineToPoint:CGPointMake(100, 200)];////    CGContextAddPath(ctx, path.CGPath);// 4.把上下文内容渲染到viewCGContextStrokePath(ctx);

    示例2画圆形

    //Center:圆心//radius:半径.//startAngle:开始角度//endAngle:结束角度//clockwise:CGPointcenter =CGPointMake(self.bounds.size.width*0.5,self.bounds.size.height*0.5);CGFloatradius =100;CGFloatstartA =0;//圆的0度角在圆的最右侧.CGFloatendA = -M_PI_2;UIBezierPath*path =  [UIBezierPathbezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];    [UIColorredColor] set];    [path stroke];

    示例3画文字,常见属性

    //我们要画的文字NSString*str =@"我们要画的文字";//开始绘制文字//    AtPoint:画在哪个点上.//Attributes:文字的属性.颜色,字体大小...NSMutableDictionary*dict = [NSMutableDictionarydictionary];//设置文字的颜色dict[NSForegroundColorAttributeName] = [UIColorredColor];//设置字体大小dict[NSFontAttributeName] = [UIFontsystemFontOfSize:50];//设置描边的颜色dict[NSStrokeColorAttributeName] = [UIColorblueColor];//设置描边的宽度dict[NSStrokeWidthAttributeName] = @1;NSShadow*shadow = [[NSShadowalloc] init];//设置阴影的偏移量shadow.shadowOffset=CGSizeMake(-10,10);//设置阴影的颜色shadow.shadowColor= [UIColorgreenColor];//设置阴影的模糊shadow.shadowBlurRadius=2;    dict[NSShadowAttributeName] = shadow;//drawAtPoint不会自动换行//    [str drawAtPoint:CGPointZero withAttributes:dict];//drawInRect它会自动的换行.[str drawInRect:self.boundswithAttributes:dict];

    图形上下文的矩阵操作

    形变操作,必须得要在添加路径之前进行

    计时器

    CADisplayLink

    //在绘图当中, 我们一般使用CADisplayLink.因为他和setNeedsDisplay调用时机是一样的,都是当下一次屏幕刷新的时候调用.

    //CADisplayLink//它是当每次屏幕刷新的时候就会调用定时器方法.(每一秒种刷新60次)CADisplayLink*link = [CADisplayLinkdisplayLinkWithTarget:selfselector:@selector(update)];//想让定时器工作,必须得要把它添加到主运行循环.[link addToRunLoop:[NSRunLoopmainRunLoop] forMode:NSDefaultRunLoopMode];

    NSTimer

    [NSTimerscheduledTimerWithTimeInterval:0.01target:selfselector:@selector(update) userInfo:nilrepeats:YES];

    注意,在实际开发中视情况选择使用

    常用的拼接函数

    新建一个起点voidCGContextMoveToPoint(CGContextRefc,CGFloatx,CGFloaty)添加新的线段到某个点voidCGContextAddLineToPoint(CGContextRefc,CGFloatx,CGFloaty)添加一个矩形voidCGContextAddRect(CGContextRefc,CGRectrect)添加一个椭圆voidCGContextAddEllipseInRect(CGContextRefcontext,CGRectrect)添加一个圆弧voidCGContextAddArc(CGContextRefc,CGFloatx,CGFloaty,CGFloatradius,CGFloatstartAngle,CGFloatendAngle,intclockwise)

    常用绘制的函数

    Mode参数决定绘制的模式

    voidCGContextDrawPath(CGContextRefc,CGPathDrawingModemode)绘制空心路径voidCGContextStrokePath(CGContextRefc)绘制实心路径voidCGContextFillPath(CGContextRefc)提示:一般以CGContextDraw、CGContextStroke、CGContextFill开头的函数,都是用来绘制路径的

    矩阵操作

    利用矩阵操作,能让绘制到上下文中的所有路径一起发生变化缩放voidCGContextScaleCTM(CGContextRefc,CGFloatsx,CGFloatsy)旋转voidCGContextRotateCTM(CGContextRefc,CGFloatangle)平移voidCGContextTranslateCTM(CGContextRefc,CGFloattx,CGFloatty)

    图片水印技术

    需求:在手机客户端app中需要用到水印技术,用户拍完照片后,可以在照片上打个水印,标识这个图片是属于哪个用户的

    实现方式:利用Quartz2D,将水印(文字、LOGO)画到图片的右下角

    核心代码

    开启一个基于位图的图形上下文UIGraphicsBeginImageContextWithOptions(CGSizesize,BOOLopaque,CGFloatscale)从上下文中取得图片(UIImage)UIImage*UIGraphicsGetImageFromCurrentImageContext();结束基于位图的图形上下文UIGraphicsEndImageContext();

    水印PPT简介

    图片水印作用:防止他人盗取图片,加一些Logo,生成一张新的图片。

    和绘图一样的生成新的图片,需要拿到上下文做事情,这里也需要拿到上下文,生成一个新的图片。

    位图上下文,在这个上下文画东西,就能输出到新的图片上。

    之前用的都是图层上下文,系统会自动创建,但是我们位图上下文,需要我们手动创建

    总结:只要不和view有关系的上下文,都需要我们手动创建。

    在哪获取图像上下文,viewDidLoad, 不需要拿到系统创建的图层上下文,没必要在drawRect方法里写,直接viewDidLoad就行了。

    UIGraphicsBeginImageContextWithOptions:(CGSize size, BOOL opaque, CGFloat scale)创建一个位图上下文,而且这种方法得到的图片最清晰。解释参数(size:新图片尺寸 opaque: YES:不透明 NO:透明 scale:0.0 不伸缩)

    绘制内容(图片,文字)

    获取图片:把位图上下文的内容生成一个图片给你。

    关闭上下文,不关闭一直占用着内存。

    显示UIImageView上

    保存图片,写到文件,UIImage不能写,需要转换成NSData二进制数据

    UIImageJPEGRepresentation:可以设置图片质量

    UIImagePNGRepresentation:把图片转换成png格式的二进制数据,png格式默认是最高清的。

    写到桌面

    图片裁剪

    图片裁剪

    PPT分析思路:先设置裁剪区域,把图片画上去,超出裁剪区域的自动裁剪掉。

    加载旧图片,根据旧图片,获取上下文尺寸。

    上下文的尺寸 = 新图片的尺寸

    开启一个多大的上下文?:和图片尺寸一样大,避免压缩图片。如果比图片尺寸小,会压缩图片。

    设置裁剪区域:正切于图片的圆

    绘制旧图片

    获取新图片

    关闭上下文

    //1.加载要裁剪的图片UIImage*image = [UIImageimageNamed:@"阿狸头像"];

    //2.开启一个跟图片尺寸一样大的位图上下文UIGraphicsBeginImageContextWithOptions(image.size,NO,0);

    //3.设置一个裁剪(宽度和高度都是等于原始图片的宽高)UIBezierPath*path = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0, image.size.width, image.size.height)];//把路径设置成裁剪区域[path addClip];

    //4.把要裁剪的图片绘制到上下文当中.[image drawAtPoint:CGPointZero];

    //5.从上下文当中生成一张新的图片UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();

    //6.关闭上下文.UIGraphicsEndImageContext();self.imageV.image= newImage;

    带圆环裁剪:在裁剪的图片外边加个小圆环。

    先画一个大圆,在设置裁剪区域,把图片画上去,超出裁剪区域的自动裁剪掉。

    加载旧图片,根据旧图片,获取上下文尺寸。

    确定圆环宽度 borderW

    上下文的尺寸 = 新图片的尺寸

    确定新的上下文尺寸: newImageW : oldImageW + 2borderW newImageH : oldImageH + 2borderW,

    绘制大圆: 1.获取上下文 2.添加路径到上下文 3.设置大圆的颜色 = 圆环的颜色 4.渲染

    设置裁剪区域,和图片尺寸一样大,只不过,x,y不一样,x=borderW,y=borderW.

    绘制旧图片

    获取新图片

    关闭上下文

    抽分类,3个参数,图片名称,圆环宽度,圆环颜色

    可以创建给UIImage添加一个分类提供方法直接使用

    //0.设置一个边框宽度//1.加载图片//2.开启一个位图上下文CGSizesize =CGSizeMake(image.size.width+2* boderW, image.size.height+2* boderW);UIGraphicsBeginImageContextWithOptions(size,NO,0);

    //3.设置一个大圆的一个填充区域UIBezierPath*path = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0, size.width, size.height)];//设置边框的颜色[color set];    [path fill];

    //4.设置一个小圆裁剪区域path = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(boderW, boderW, image.size.width, image.size.height)];//把小圆设置成裁剪区域[path addClip];

    //5.把原始图片绘制到上下文当中[image drawInRect:CGRectMake(boderW, boderW, image.size.width, image.size.height)];

    //6.生成一张新的图片UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();

    //7.关闭上下文.UIGraphicsEndImageContext();returnnewImage;

    图片截屏

    屏幕截图:把屏幕的内容截屏生成一张新的图片

    通常开发中,都是把控制器的内容截屏,生成新的图片

    控制器根据view显示

    view根据layer图层显示

    把layer渲染到位图上下文

    注意:图层只能用渲染,图片和文字可以用draw

    渲染在新的图片

    开启图片上下文,和视图一样的尺寸

    写入桌面

    抽分

    //懒加载,当使用才创建,可以节省内存-(UIView*)cover{if(_cover ==nil) {//创建一个遮盖UIView*cover = [[UIViewalloc] init];        cover.backgroundColor= [UIColorblackColor];        cover.alpha=0.7;        _cover = cover;        [self.viewaddSubview:cover];    }return_cover;}

    - (void)viewDidLoad {    [superviewDidLoad];// Do any additional setup after loading the view, typically from a nib.}

    //当手指拖动的时候调用- (IBAction)pan:(UIPanGestureRecognizer*)pan {//获取当前手指所在的点CGPointcurP = [pan locationInView:self.imageV];//起点if(pan.state==UIGestureRecognizerStateBegan){//添加遮盖,设置遮盖的起点.self.startP= curP;    }elseif(pan.state==UIGestureRecognizerStateChanged){//移动,确定遮盖的尺寸//x轴偏移量CGFloatoffsetX = curP.x-self.startP.x;//Y 轴的偏移量CGFloatoffsetY = curP.y-self.startP.y;//改变遮盖的尺寸self.cover.frame=CGRectMake(self.startP.x,self.startP.y, offsetX, offsetY);    }else if(pan.state==UIGestureRecognizerStateEnded){//松开//做裁剪

    //开启一个上下文.(跟图片一样大的上下文.)UIGraphicsBeginImageContextWithOptions(self.imageV.bounds.size,NO,0);

    //在上下文当中设置一个裁剪区域(就是当前遮盖的frame)UIBezierPath*path = [UIBezierPathbezierPathWithRect:self.cover.frame];

    //设置裁剪区域[path addClip];//把UIImageView当中的图片绘制到上下文当中.

    //获取当前上下文CGContextRefctx =UIGraphicsGetCurrentContext();      [self.imageV.layerrenderInContext:ctx];

    //生成一张新的图片.UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();

    //把位图上下文关闭掉UIGraphicsEndImageContext();

    //把UIImageView当中的图片重新赋值self.imageV.image= newImage;//移除遮盖[self.coverremoveFromSuperview];    }}

    手势解锁

    分析界面有几个控件:背景:UIImageView 白色圆圈:按钮(点击他,会出现另外一种图片,按钮可以设置不同状态下的图片。)单独视图:(画线是有范围的,当超出view就不能画线了)

    HMLoadView:自定义视图,在视图一创建的时候,就添加9个按钮。

    在initWithCoder,initWithFrame方法添加按钮。

    九宫格布局:

    tolcol =3计算row,col 按钮的x,y跟col,row有关系,col = i % tolcol row = i / tolcol  计算边距 margin = (view.bounds.size.width- tolcol * btnW) / (tolcol +1)  btnX = margin + (btnW + margin) * col  btnY = (btnW + margin) * row

    圆的选中

    点击按钮就为选中的图片怎么做?监听按钮点击。

    不能addTarget: 不能及时显示选中图片。

    监听touchBegin,判断点在不在按钮的frame上。

    touchBegin不调用?原因:事件交给按钮处理,应该把事件交给解锁视图。让按钮不接收事件。

    设置按钮不允许交互,2个用处:1.不接收事件 2.取消高亮效果 一举两得

    遍历所有按钮,看触摸点在哪个按钮上,就选中谁,CGRectContainsPoint 传入的参数必须是同一个坐标系

    实现touchMove方法:因为手指移动的时候,也需要判断点在不在按钮上。

    抽方法,因为touchMove的方法里,也需要做同样的事情。

    1>pointWithTouches根据touches集合取出触摸点2>buttonWithPoint根据触摸点,获取触摸按钮

    圆的连线

    被选中按钮之间都需要连线,还有一个多余的线

    先把选中的按钮全部连线,因为多余的那根线是从最后一个按钮的圆心开始画,手指移动在哪就画哪。

    搞个数组保存下所有选中按钮,在drawRect方法中遍历所有按钮,连线

    UIBezierPath画线,不需要上下文。

    需要多少个UIBezierPath对象?一个,路径都是连续的,不相连的,才需要创建新的UIBezierPath。

    遍历数组,描述路径 1> 起点:第一个按钮的圆心 2> 添加一根线到其他按钮的圆心

    设置路径的颜色和线宽

    把所有路径都描述完就,渲染一次就够了。

    [path stroke] 就能渲染到视图上了。

    画多余的那根线,记住手指移动的位置。

    setNeedDisplay 因为drawRect只会调用一次,需要每次手指移动的时候,都需要重绘。

    touchBegin不调用setNeedDisplay,因为重绘也没用,只有起点。

    lineJoinStyle:有尖尖的东西,线段连接样式的问题,设置为平的。

    已经选中的按钮,不需要再次选中,和画线

    手指抬起,取消所有选中按钮,并且清空数组,清空线条

    drawRect判断下没有选中数组,不需要画线。

    如何判断用户是否输入正确?给选中按钮绑定tag,遍历所有选中按钮,把tag拼接成一个字符串。

    画板

    分析控件:ToolBar(不需要管里面子控件的frame),画板view,自定义工具条(方便屏幕适配,能迅速固定里面子控件的位置)

    自动布局,四个约束确定一个控件

    蓝色按钮:左,右,下,高固定

    橘色按钮:右,下固定,宽度和高度和蓝色按钮相等。

    绿色按钮:右,下,宽度和高度和橘色按钮相等

    绘图思路:先描述路径,在渲染,需要画很多线条,最好每个线条保存到一个路径里面。

    绘图功能

    touchBegin设置画线起点:开始触摸的点

    创建UIBezierPath,贝塞尔路径才能设置起点

    touchMove:手指移动到哪就画哪,addLine到移动的点

    setNeedDisplay,路径描述完了,就渲染到视图就好了。

    drawRect方法每次都会把之前的清掉,重新绘制

    搞个数组保存上一次的,绘制多条线

    设置线宽:每次滑动滑块,就改变下一次路径的宽度

    监听滑块的值,把值传递给paintView,设置路径的线宽。

    不能在drawRect写,会导致所有路径都是一个线宽,应该是每个路径都记录自己的线宽,而且线宽只需要设置一次,在路径一创建的时候就设置。

    设置颜色:

    自定义UIBezierPath,保存颜色,实现一条路径对应一个颜色 辅助功能:

    清屏:清空所有路径数组

    撤销:移除最后一条路径

    橡皮擦:设置画笔为白色

    保存:

    1> 把画板内容截屏

    2> 把图片保存到相册 UIImageWriteToSavedPhotosAlbum

    3> 保存相册的回调方法不能乱写,必须按照规定 image:didFinishSavingWithError:contextInfo:

    照片选择

    1> 通常都是去相册里去照片

    2> UIImagePickerController,就可以去手机相册了

    3> 用Modal,没有导航控制器,不能push

    4> 设置代理,获取图片

    5> 把图片传递给paintView

    6> 添加到路径,然后重绘。:画图片也需要顺序的

    照片处理

    1>搞一个和画板一样的透明view,里面搞个UIImageView来显示我们从照片库选择的图片,然后对UIImageView进行旋转,缩放等操作

    2> UImageView不能放在layoutSubViews里面设置尺寸,因为要设置他的形变,默认会调用他父类的layoutSubViews,导致一些莫名其妙的原因

    3> 在传图片的时候设置他的尺寸,和位置,让他和图片一样的尺寸,显示在中间

    4> 长按操作:在长按结束的时候,做操作

    1.默认高亮状态,先变浅在恢复,设置alpha

    2.动画结束后,把自己截屏,传给控制器里,在交给paintView显示

    3.需要移除父视图,使命完成了,而且不移除,不能绘制东西,永远添加到paintView上面

    相关文章

      网友评论

        本文标题:Quartz2D

        本文链接:https://www.haomeiwen.com/subject/ncugcttx.html