前言
iOS支持两套图形API族:Core Graphics和OpenGL ES。OpenGL ES是跨平台的图形API,属于OpenGL的一个简化版本。而QuartZ 2D是苹果公司开发的一套API,它是Core Graphics Framewore的一部分。
本文中主要是对Core Graphics框架的学习总结。
对于一个初学者来说,UIKit和Core Graphics两个支持绘图的框架常常令我们感到迷惑。
UIKit
UIKit是一组Objective-C API,为线条图形、Quartz图像和颜色操作提供Objective-C封装,并提供2D绘制、图像处理及用户接口级别的动画。
UIKit包含UIBezierPath(绘制线、角度、椭圆及其它图形)、UIImage(显示图像)、UIColor(颜色操作)、UIFont和UIScreen(提供字体和屏幕信息)等类,它在位图图形环境、PDF图形环境上进行绘制和操作功能,也提供对标准视图的支持,也提供对打印功能的支持。
使用UIKit,你只能在当前上下文中绘图。
Core Graphics
这是一个绘图专用的API族,它经常被称为QuartZ或者QuartZ 2D。Core Graphics 是iOS上所有绘图功能的基石,包括UIKit(UIKit是对Core Graphics的进一步封装)。
使用Core Graphics之前需要指定一个用于绘图的图形上下文(CGContextRef),这个图形上下文会在每个绘图函数中都会用到。
至此,我们了解了这两个框架的基本关系,而这两个框架中都提到了图形上下文(图形场景)。获得一个图形上下文是我们完成绘图任务的第一步,可以将图形上下文理解为一块画布。那么下边介绍三种获得图形上下文的方法:
- (void)drawRect:(CGRect)rect方法
在此方法中,UIKit框架可以直接绘图,而Core Graphics可以通过UIGraphicsGetCurrentContext函数获取当前的图形上下文。
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
在此方法中,直接传入了图形上下文,但是该上下文不一定是当前的图形上下文,所以在UIKit框架下绘图时,需要调用UIGraphicsPushContext()函数将该上下文变为当前上下文,结束时调用UIGraphicsPopContext()函数还原上下文。而Core Graphics框架下可以直接使用该上下文。
UIGraphicsBeginImageContextWithOptions函数
此函数获取图形上下文的规则跟drawRect:方法是一样的。利用该上下文,你就可以在其上进行绘图,并生成图片。调用UIGraphicsGetImageFromCurrentImageContext函数可从当前上下文中获取一个UIImage对象。记住在你所有的绘图操作后别忘了调用UIGraphicsEndImageContext函数关闭图形上下文。
这里主要解释一下三个参数的含义。第一个参数表示所要创建的图片尺寸;第二个参数用来指定生成图片的背景是否为不透明,YES表示不透明,则得到的是黑色的背景;第三个参数制定生成图片的缩放因子,这个缩放因子与UIImage的scale属性所指的含义是一致的。0则表示让图片的缩放因子根据屏幕的分辨率而变化。
基于两大绘图框架和三种获取图形上下文的方法的六种绘图形式
举例说明:绘制一个蓝色的圆
第一种绘图形式:基于UIKit框架(即使用UIBezierPath)在UIView的子类方法drawRect:中实现。
- (void) drawRect: (CGRect) rect {
UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
}
第二种绘图形式:基于Core Graphics框架在UIView的子类方法drawRect:中实现。
- (void) drawRect: (CGRect) rect {
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
CGContextFillPath(con);
}
第三种绘图形式:基于UIKit框架(即使用UIBezierPath) 在UIView的子类方法drawLayer:inContext:中实现。
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
[super drawLayer:layer inContext:ctx];
UIGraphicsPushContext(ctx);
UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
UIGraphicsPopContext();
}
PS:同时此方法必须重写drawRect:方法,否则上边的方法不会被调用。
详细了解这块的调用逻辑可以看这篇文章
iOS的绘图之drawRect和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对象的代码可以在任何地方被使用,它没有上述绘图方法那样的限制。
第五种绘图形式: 使用UIKit实现:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0);
UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
[[UIColor blueColor] setFill];
[p fill];
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
第六种绘图形式: 使用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以后苹果让绘图操作在第二个线程中执行解决了此问题。
写在最后的话
从开始研究这个框架看了很多文章,收获很多,慢慢从这些文章中一点点的找到它们之间的关系,这个过程是曲折的,但也是喜悦的。后续还会更新,本篇文章只是开始,一点点皮毛。