Core Graphics Framework
是一套基于C的API框架,使用了Quartz
作为绘图引擎。它提供了低级别、轻量级、高保真度的2D渲染。该框架可以用于基于路径的绘图、变换、颜色管理、脱屏渲染,模板、渐变、遮蔽、图像数据管理、图像的创建、遮罩以及PDF文档的创建、显示和分析。
Core Graphics
这是一个绘图专用的API族,它经常被称为QuartZ
或QuartZ 2D
。Core Graphics
是iOS上所有绘图功能的基石,包括UIKit
。
-
使用
Core Graphics
之前需要指定一个用于绘图的图形上下文(CGContextRef)
,这个图形上下文会在每个绘图函数中都会被用到。如果你持有一个图形上下文context
:参数,那么你等同于有了一个图形上下文,这个上下文也许就是你需要用来绘图的那个。如果你当前处于UIGraphicsBeginImageContextWithOptions
函数或drawRect
:方法中,并没有引用一个上下文。为了使用Core Graphics
,你可以调用UIGraphicsGetCurrentContext
函数获得当前的图形上下文。 -
至此,我们有了两大绘图框架的支持以及三种获得图形上下文的方法
(drawRect:、drawRect: inContext:、UIGraphicsBeginImageContextWithOptions)
。那么我们就有6种绘图的形式。如果你有些困惑了,不用怕,我接下来将说明这6种情况。无需担心还没有具体的绘图命令,你只需关注上下文如何被创建以及我们是在使用UIKit
还是Core Graphics
。
第一种绘图形式:
在UIView
的子类方法drawRect:中绘制一个蓝色圆,使用UIKit
在Cocoa
为我们提供的当前上下文中完成绘图任务。
- (void) drawRect: (CGRect) rect
{
UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
}
第二种绘图形式:
使用Core Graphics
实现绘制蓝色圆。
- (void) drawRect: (CGRect) rect
{
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
CGContextFillPath(con);
}
第三种绘图形式:
我将在UIView
子类的drawLayer:inContext:
方法中实现绘图任务。drawLayer:inContext:
方法是一个绘制图层内容的代理方法。为了能够调用drawLayer:inContext:
方法,我们需要设定图层的代理对象。但要注意,不应该将UIView
对象设置为显示层的委托对象,这是因为UIView
对象已经是隐式层的代理对象,再将它设置为另一个层的委托对象就会出问题。轻量级的做法是:编写负责绘图形的代理类。在MyView.h
文件中声明如下代码:
@interface MyLayerDelegate : NSObject
@end
然后MyView.m
文件中实现接口代码:
@implementation MyLayerDelegate
- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx
{
UIGraphicsPushContext(ctx);
UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
UIGraphicsPopContext();
}
@end
直接将代理类的实现代码放在MyView.m
文件的#import
代码的下面,这样感觉好像在使用私有类完成绘图任务(虽然这不是私有类)。需要注意的是,我们所引用的上下文并不是当前上下文,所以为了能够使用UIKit
,我们需要将引用的上下文转变成当前上下文。
因为图层的代理是assign
内存管理策略,那么这里就不能以局部变量的形式创建MyLayerDelegate
实例对象赋值给图层代理。这里选择在MyView.m
中增加一个实例变量,因为实例变量默认是strong
:
@interface MyView () {
MyLayerDelegate* _layerDeleagete;
}
@end
使用该图层代理:
MyView *myView = [[MyView alloc] initWithFrame: CGRectMake(0, 0, 320, 480)];
CALayer *myLayer = [CALayer layer];
_layerDelegate = [[MyLayerDelegate alloc] init];
myLayer.delegate = _layerDelegate;
[myView.layer addSublayer:myLayer];
[myView setNeedsDisplay]; // 调用此方法,drawLayer: inContext:方法才会被调用。
第四种绘图形式:
使用Core Graphics
在drawLayer:inContext:
方法中实现同样操作,代码如下:
- (void)drawLayer:(CALayer*)lay inContext:(CGContextRef)con
{
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
CGContextFillPath(con);
}
最后,演示UIGraphicsBeginImageContextWithOptions
的用法,并从上下文中生成一个UIImage
对象。生成UIImage
对象的代码可以在任何地方被使用,它没有上述绘图方法那样的限制。
第五种绘图形式:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0);
UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
解释一下UIGraphicsBeginImageContextWithOptions
函数参数的含义:第一个参数表示所要创建的图片的尺寸;第二个参数用来指定所生成图片的背景是否为不透明,如上我们使用YES
而不是NO
,则我们得到的图片背景将会是黑色,显然这不是我想要的;第三个参数指定生成图片的缩放因子,这个缩放因子与UIImage
的scale
属性所指的含义是一致的。传入0则表示让图片的缩放因子根据屏幕的分辨率而变化,所以我们得到的图片不管是在单分辨率还是视网膜屏上看起来都会很好。
第六种绘图形式:
使用Core Graphics实现:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0);
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
CGContextFillPath(con);
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIKit
和Core Graphics
可以在相同的图形上下文中混合使用。在iOS 4.0之前,使用UIKit
和UIGraphicsGetCurrentContext
被认为是线程不安全的。而在iOS4.0以后苹果让绘图操作在第二个线程中执行解决了此问题。

参考资料
土梦博客
网友评论