iOS绘图教程(个人学习总结)

摘要:
重复利用路径的相关函数和数据类型们有了两大绘图框架的支持以及三种获得图形上下文的方法。无需担心还没有具体的绘图命令,你只需关注上下文如何被创建以及我们是在使用UIKit还是CoreGraphics。但要注意,不应该将UIView对象设置为显示层的委托对象,这是因为UIView对象已经是隐式层的代理对象,再将它设置为另一个层的委托对象就会出问题。轻量级的做法是:编写负责绘图形的代理类。

iOS绘图教程:http://www.cocoachina.com/applenews/devnews/2014/0115/7703.html

本篇博文是为了梳理学习过程中得框架,上边链接是cocoachina的教程,更详细一些

iOS支持两套图形API族:Core Graphics/QuartZ 2D 和OpenGL ES

路径用于描述由一序列线和Bézier曲线构成的2D几何形状

Core Graphics中也有一些用于创建简单路径(比如矩形和椭圆形)的便利函数。对于更为复杂的路径,必须用Core Graphics框架提供的函数自行创建

Bézier曲线是法国数学家“贝塞尔”在工作中发现,任何一条曲线都可以通过与它相切的控制线两端的点的位置来定义。

Bézier曲线

wps_clip_image-26605

两种方法创建弧度 第一种

void CGContextAddArc (
CGContextRef c,
CGFloat x, //圆心的x坐标
CGFloat y, //圆心的x坐标
CGFloat radius, //圆的半径
CGFloat startAngle, //开始弧度
CGFloat endAngle, //结束弧度
int clockwise //0表示顺时针,1表示逆时针
);

第二种

void CGContextAddArcToPoint (
CGContextRef c,
CGFloat x1, //端点1的x坐标
CGFloat y1, //端点1的y坐标
CGFloat x2, //端点2的x坐标
CGFloat y2, //端点2的y坐标
CGFloat radius //半径
);
Curves
画曲线,一般是一条直线,然后定义几个控制点,使直线变弯曲。
三次曲线函数
void CGContextAddCurveToPoint(
CGContextRef c,
CGFloat cp1x, //控制点1 x坐标
CGFloat cp1y, //控制点1 y坐标
CGFloat cp2x, //控制点2 x坐标
CGFloat cp2y, //控制点2 y坐标
CGFloat x, //直线的终点 x坐标
CGFloat y //直线的终点 y坐标
);
二次曲线函数
void CGContextAddQuadCurveToPoint (
CGContextRef c,
CGFloat cpx, //控制点 x坐标
CGFloat cpy, //控制点 y坐标
CGFloat x, //直线的终点 x坐标
CGFloat y //直线的终点 y坐标
);
Ellipses
void CGContextAddEllipseInRect (
CGContextRef context,
CGRect rect //一矩形
);
如果矩形是一个正方形,那么画出来就是一个圆
执行完函数貌似current point不会变化,没有具体测试过
Rectangles
void CGContextAddRect (
CGContextRef c,
CGRect rect
);
一次性画出多个矩形
void CGContextAddRects (
CGContextRef c,
const CGRect rects[],
size_t count
);
需要注意的是,画矩形有一些特别,current point没有发生变化
Creating a Path
调用函数 CGContextBeginPath 开始创建路径,线调用函数CGContextMoveToPoint设置起点
然后开始画自己想画的路径,注意一下几点:
1.Lines, arcs, and curves,是从current point开始的
2.假如想封闭一条路径,那么调用函数 CGContextClosePath 把当前点和起点连接起来
3.当在画 arcs的时候,Quartz会画一条线从current point 到 starting point
4.画矩形的时候不会有第三条那这样的的一条直线
5.创建完路径后,必须调用 painting 函数 fill or stroke the path,不然不会画上面东东在相应的设备上】
6.开始创建一个新的路径的时候,使用函数 CGContextBeginPath。
重复利用路径的相关函数和数据类型
们有了两大绘图框架的支持以及三种获得图形上下文的方法(drawRect:、drawRect: inContext:、UIGraphicsBeginImageContextWithOptions)。那么我们就有6种绘图的形式。如果你有些困惑了,不用怕,我接下来将说明这6种情况。无需担心还没有具体的绘图命令,你只需关注上下文如何被创建以及我们是在使用UIKit还是Core Graphics。
第一种绘图形式:在UIView的子类方法drawRect:中绘制一个蓝色圆,使用UIKit在Cocoa为我们提供的当前上下文中完成绘图任务。
  1. -(void)drawRect:(CGRect)rect{
  2. UIBezierPath*p=[UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
  3. [[UIColorblueColor]setFill];
  4. [pfill];
  5. }

第二种绘图形式:使用Core Graphics实现绘制蓝色圆。

  1. -(void)drawRect:(CGRect)rect{
  2. CGContextRefcon=UIGraphicsGetCurrentContext();
  3. CGContextAddEllipseInRect(con,CGRectMake(0,0,100,100));
  4. CGContextSetFillColorWithColor(con,[UIColorblueColor].CGColor);
  5. CGContextFillPath(con);
  6. }

上边两种方法对比 第一种是使用UIBezierPath 这个方法 自动的在当前view中画出图形

第二种方法 是使用core Graphics 是获得上下文之后进行绘图 CGContextRef

第三种绘图形式:我将在UIView子类的drawLayer:inContext:方法中实现绘图任务。drawLayer:inContext:方法是一个绘制图层内容的代理方法。为了能够调用drawLayer:inContext:方法,我们需要设定图层的代理对象。但要注意,不应该将UIView对象设置为显示层的委托对象,这是因为UIView对象已经是隐式层的代理对象,再将它设置为另一个层的委托对象就会出问题。轻量级的做法是:编写负责绘图形的代理类。在MyView.h文件中声明如下代码:

  1. @interfaceMyLayerDelegate:NSObject
  2. @end

然后MyView.m文件中实现接口代码:

  1. @implementationMyLayerDelegate
  2. -(void)drawLayer:(CALayer*)layerinContext:(CGContextRef)ctx{
  3. UIGraphicsPushContext(ctx);
  4. UIBezierPath*p=[UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
  5. [[UIColorblueColor]setFill];
  6. [pfill];
  7. UIGraphicsPopContext();
  8. }
  9. @end
直接将代理类的实现代码放在MyView.m文件的#import代码的下面,这样感觉好像在使用私有类完成绘图任务(虽然这不是私有类)。需要注意的是,我们所引用的上下文并不是当前上下文,所以为了能够使用UIKit,我们需要将引用的上下文转变成当前上下文。
因为图层的代理是assign内存管理策略,那么这里就不能以局部变量的形式创建MyLayerDelegate实例对象赋值给图层代理。这里选择在MyView.m中增加一个实例变量,因为实例变量默认是strong:
  1. @interfaceMyView(){
  2. MyLayerDelegate*_layerDeleagete;
  3. }
  4. @end
使用该图层代理:
  1. MyView*myView=[[MyViewalloc]initWithFrame:CGRectMake(0,0,320,480)];
  2. CALayer*myLayer=[CALayerlayer];
  3. _layerDelegate=[[MyLayerDelegatealloc]init];
  4. myLayer.delegate=_layerDelegate;
  5. [myView.layeraddSublayer:myLayer];
  6. [myViewsetNeedsDisplay];//调用此方法,drawLayer:inContext:方法才会被调用。
第四种绘图形式:使用Core Graphics在drawLayer:inContext:方法中实现同样操作,代码如下:
  1. -(void)drawLayer:(CALayer*)layinContext:(CGContextRef)con{
  2. CGContextAddEllipseInRect(con,CGRectMake(0,0,100,100));
  3. CGContextSetFillColorWithColor(con,[UIColorblueColor].CGColor);
  4. CGContextFillPath(con);
  5. }
最后,演示UIGraphicsBeginImageContextWithOptions的用法,并从上下文中生成一个UIImage对象。生成UIImage对象的代码并不需要等待某些方法被调用后或在UIView的子类中才能去做。
第五种绘图形式:使用UIKit实现:
  1. UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100),NO,0);
  2. UIBezierPath*p=[UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
  3. [[UIColorblueColor]setFill];
  4. [pfill];
  5. UIImage*im=UIGraphicsGetImageFromCurrentImageContext();
  6. UIGraphicsEndImageContext();

解释一下UIGraphicsBeginImageContextWithOptions函数参数的含义:第一个参数表示所要创建的图片的尺寸;第二个参数用来指定所生成图片的背景是否为不透明,如上我们使用YES而不是NO,则我们得到的图片背景将会是黑色,显然这不是我想要的;第三个参数指定生成图片的缩放因子,这个缩放因子与UIImage的scale属性所指的含义是一致的。传入0则表示让图片的缩放因子根据屏幕的分辨率而变化,所以我们得到的图片不管是在单分辨率还是视网膜屏上看起来都会很好。

第六种绘图形式:使用Core Graphics实现:
  1. UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100),NO,0);
  2. CGContextRefcon=UIGraphicsGetCurrentContext();
  3. CGContextAddEllipseInRect(con,CGRectMake(0,0,100,100));
  4. CGContextSetFillColorWithColor(con,[UIColorblueColor].CGColor);
  5. CGContextFillPath(con);
  6. UIImage*im=UIGraphicsGetImageFromCurrentImageContext();
  7. UIGraphicsEndImageContext();
UIKit和Core Graphics可以在相同的图形上下文中混合使用。在iOS 4.0之前,使用UIKit和UIGraphicsGetCurrentContext被认为是线程不安全的。而在iOS4.0以后苹果让绘图操作在第二个线程中执行解决了此问题。
之后就不写了~可以看下原文

免责声明:文章转载自《iOS绘图教程(个人学习总结)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇基于Unity的AR开发初探:发布AR应用到Android平台理解杀进程的实现原理下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

kubernetes配置(kubeconfig)对多集群的访问

配置对多集群的访问 本文展示如何使用配置文件来配置对多个集群的访问。 在将集群、用户和上下文定义在一个或多个配置文件中之后,用户可以使用kubectl config use-context命令快速地在集群之间进行切换。 注意:用于配置集群访问的文件有时被称为kubeconfig 文件。 这是一种引用配置文件的通用方式,并不意味着存在一个名为kubecon...

linux内核分析笔记----中断和中断处理程序

       中断还是中断,我讲了很多次的中断了,今天还是要讲中断,为啥呢?因为在操作系统中,中断是必须要讲的..        那么什么叫中断呢, 中断还是打断,这样一说你就不明白了。唉,中断还真是有点像打断。我们知道linux管理所有的硬件设备,要做的第一件事先是通信。然后,我们天天在说一句话:处理器的速度跟外围硬件设备的速度往往不在一个数量级上,...

iOS数据持久化的方式

概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案: plist文件(属性列表) preference(偏好设置) NSKeyedArchiver(归档) SQLite 3 CoreData 沙盒 在介绍各种存储方法之前,有必要说明以...

Spring Boot中集成Spring Security 专题

  check to see if spring security is applied that the appropriate resources are permitted: @Configuration public class WebSecurityConfiguration extends WebSecurityConfigurer...

RTEMS 进程切换分析(基于i386体系)

在支持多任务操作系统中,进程切换是不可避免的,以使进程能在单个CPU上并发执行。进程的调度涉及到的东西较多,例如调度的时机、调度的策略等等,在这里我们只讨论RTEMS任务调度中进程切换的细节,通过分析以明白操作系统如何做到使一个CPU的使用权如何从一个任务上切换到另一个任务。 下面假设两个任务TASK1和TASK2,当前正在执行的任务executing =...

SpringApplication类-1

SpringApplication类提供了一种方便的方法来引导从main()方法启动的Spring应用程序。在许多情况下,可以委托给静态SpringApplication.run方法,如下例所示: public static voidmain(String[] args) { SpringApplication.run(MySpringConfig...