iOS绘图

在iOS中常用有三套绘图API。一个是UIKit提供的高层API,一个是CoreGraphics提供的C语言层的API,最后一个是OpenGL ES提供的API。

iOS的绘图逻辑代码需要放在UIView的drawRect:方法里面实现,所以绘图只能发生在UIView上面。

绘图后如果我们想要显示图像可以调用

setNeedsDisplaysetNeedsDisplayInRect:这两个方法是用来标示一个视图是否需要进行重绘,这里标示重绘并不会马上重新绘制,而是等到该RunLoop上面的任务执行完成后才回执行重绘。

触发重绘有以下几种情况:

1.当遮挡你的视图得其它视图被移动或者删除操作得时候;

2.将视图的hidden属性声明设置为No,使其从隐藏状态变为可见;

3.将视图滚出屏幕,然后重新回到屏幕;

4.显示调用视图的setNeedsDisplay或者setNeedsDisplayInRect:方法;

ios绘图周期分析

通过下面这个例子来分析

progressView.hidden = NO;

[self doSomethingTimeConsuming];

progressView.hidden = YES;

第一行代码progress.hidden = NO;根本没有效果,这行代码不会使进度视图在执行耗时操作时显示出来。无论这个方法运行多久,都不会看到视图显示出来。

所有的绘制都发生在主线程,只要代码运行在主线程,就没有东西可以绘制。这就是不要在主线程中执行长时间运行操作的一个原因。这不仅会阻碍绘制更新,还会阻碍事件处理(比如响应触摸事件)。只要代码在主线程上,应用对于用户其实就是“功能挂起的”。如果主线程例程返回足够快,这些变化根本就察觉不到。
你可能会想:“那我就在后台线程运行我的绘图指令。”通常是无法做到这一点的,因为对于当前的UIKit上下文来说绘图不是线程安全的。任何在后台线程修改视图的尝试都会导致未定义的行为,包括绘制出错或崩溃。
这个行为并不是需要解决的问题。绘图时间实质是ios在有限的硬件上渲染复杂绘图的功能。

UIKit绘图

比较简单,这里就说一些常用API。

设置画笔颜色:

画笔颜色分为描边颜色和填充颜色,都是用UIColor的API设置的。在drawRect方法中调用

[[UIColor anyColor] setFill]设置填充颜色。
[[UIColor anyColor] setStroke]设置描边颜色。

设置绘图区域:

UIRectFill(CGRect rect),填充某一区域 。
UIRectFrame(CGRect rect),矩形描边函数。
UIBezierPath,绘图路径类,包括了线段、弧线、矩形、圆形等等。

其中UIBezierPath可以定制主来很多很复杂的图形,这里就不具体的说UIBezierPath的api了,使用起来比较简单直接看文档就可以。

绘制图像

UIImage提供了自己的绘图方法。显示UIImage,除了添加到UIImageView中,还可以直接画到UIView上面,常用API如下:

-(void)drawAtPoint:(CGPoint)point;在某个点绘制
-(void)drawInRect:(CGRect)rect;绘制到某个矩形中
-(void)drawAsPatternInRect;绘制到某个矩形中并平铺

绘制文字

NSString的category同样提供了绘制文字的功能

-(void)drawAtPoint:(CGPoint)point withAttributes:(NSDictionary *)attrs,文本在制定点用属性绘制。
-(void)drawInRect:(CGRect)rect withAttributes:(NSDictionary *)attrs,文本在指定的矩形里绘制。

它们都可以用attrs,这和NSAttributedString很像。

CoreGraphics绘图

绘图上下文CGContextRef,CoreGraphics很类似java、C#等,需要有一个绘图上下文。

上下文中保存了要绘制内容的信息,要获得上下文需要调用。

CGContextRef UIGraphicsGetCurrentContext(void)

常用API

移动点
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)
画线
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
闭合路径
void CGContextClosePath(CGContextRef c)
绘制路径
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
设置描边颜色
void CGContextSetStrokeColorWithColor(CGContextRef c, CGColorRef color)
设置填充颜色
void CGContextSetFillColorWithColor(CGContextRef c, CGColorRef color)
绘制贝塞尔曲线,这里的参数分别是两个控制点和交点的左边,大家可以自行百度贝塞尔曲线定义
void CGContextAddCurveToPoint(CGContextRef c, CGFloat cp1x,CGFloat cp1y, CGFloat cp2x, CGFloat cp2y, CGFloat x, CGFloat y)
保存上下文
void CGContextSaveGState(CGContextRef c)
读取上下文
void CGContextRestoreGState(CGContextRef c)

常用API有很多这里只列举上面这些,剩下的可以查下API

CoreGraphics坐标系

CoreGraphics坐标系和我们平时用UIKit的坐标系是不样的,CoreGraphics的左下角为(0,0)点,而UIKit的左上角为(0,0)点。

所以我们在开发的时候,一般会先同步坐标系(CoreText也需要这么操作),应该这样写

CGContextTranslateCTM(context, 0, img.size.height);平移变化
CGContextScaleCTM(context,1,-1);缩放变换

先利用平移变换上移一个视图大小,然后在用缩放变化把高度设-1进行以x为轴的对称变换。

变换

接下来说说变换,绘图是有很多矩阵变换的,其中常用的有以下:

1.平移变换

2.缩放变换

3.旋转变换

4.x轴对称变换

5.y轴对称变换

6.坐标原点对称变换

矩阵变换CTM

CoreGraphics中提供了很多矩阵变换的API,主要有

CGContextRoatateCMT,旋转CTM,旋转变换;
CGContextScaleCTM,缩放变换;
CGContextTranslateCTM,平移变换。

仿射变换affine

仿射变换是可以重用的变换,通过多次的矩阵乘法得到变换矩阵。

CGAffineMakeRotation,创建新的旋转矩阵;
CGAffineMakeScale,创建新的缩放矩阵;
CGAffineMakeTranslation,创建新的平移矩阵;
CGAffineTransform,仿射矩阵,可以经过多次变换;
CGAffineTransformRotate,旋转矩阵;
CGAffineTransformScale,缩放矩阵;
CGAffineTransformTranslate,平移矩阵;
CGContextConcatCTM,连接到CTM变换。

我们可以创建一个CGAffineTransform,然后经过多次的仿射变换后连接到CTM进行显示。

本博客所有内容采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可

转载文章请注明:iOS绘图 - https://yimouleng.com/2014/11/25/ios%e7%bb%98%e5%9b%be/

分类: iOS开发

丶伊眸冷

静水流深,沧笙踏歌;三生阴晴圆缺,一朝悲欢离合。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

我不是机器人*