使用Quartz绘制文本、图片、PDF

PDF

Quartz创建的PDF可以直接使用Preview进行预览。

显示

步骤

  • 坐标轴翻转
  • 获取CGPDFPageRef对象
  • 显示在界面上

坐标轴翻转

PDF绘制需要从左下角为原点的坐标系。因此需要坐标轴翻转。

1
2
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

获取CGPDFPageRef

显示第一页。

1
CGPDFPageRef page = CGPDFDocumentGetPage(self.pdfDocument, 1);

需要一个CGPDFDocumentRef对象,那么就创建一个获取CGPDFDocumentRef对象的方法。

1
2
3
4
5
6
7
8
- (CGPDFDocumentRef)pdfDocument {
if (_pdfDocument == NULL) {
CFURLRef pdfURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("quartz.pdf"), NULL, NULL);
_pdfDocument = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
CFRelease(pdfURL);
}
return _pdfDocument;
}

显示在界面上

1
CGContextDrawPDFPage(context, page);

CGContextConcatCTM、CGPDFPageGetDrawingTransform

发现PDF显示不全,那么为了让PDF能够全部显示在屏幕上,那么就需要使用它CGPDFPageGetDrawingTransformCGContextConcatCTM

1
2
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFArtBox, self.bounds, 0, true);
CGContextConcatCTM(context, pdfTransform);

CGPDFPageGetDrawingTransform提供了一种简单的方法转换PDF页面,这将缩小,包含旋转以达到正确的显示PDF页面。

内存管理

使用Core Graphics是非常小心的,稍不注意就容易发生内存泄露。

1
2
3
- (void)dealloc {
CGPDFDocumentRelease(_pdfDocument);
}

创建

暂时还无法把内容写入PDF中,如果您知道,请告诉我,感激不尽。

何去何从?

可以做一个PDF Reader 阅读器App。

图片

使用UIImage绘图

这个是通过UIView的drawInRect进行绘图。

1
[[UIImage imageNamed:@"quartz"] drawInRect:CGRectMake(10, 100, 80, 80)];

原始大小

从坐标原点开始绘画的,即Point = {0,0};

1
[[UIImage imageNamed:@"quartz"] drawAtPoint:CGPointZero];

平铺

在给定的Rect中,平铺图片,图片显示原始大小,因此如果Rect的Size大于图片Size,界面会出现多张图片。

1
[[UIImage imageNamed:@"quartz"] drawAsPatternInRect:rect];

裁剪

一定要在绘制之前设置。

1
UIRectClip(CGRectMake(100, 100, 100, 100));

使用CGImage绘图

相对于使用UIImage绘图,CGImage绘图就相当麻烦。

  • 创建CGImage
  • 绘制图片

创建CGImage

1
2
3
4
5
6
7
- (CGImageRef)image {
if (_image == NULL) {
UIImage *image = [UIImage imageNamed:@"quartz"];
_image = CGImageRetain(image.CGImage);
}
return _image;
}

使用CGContextDrawImage绘制

1
2
CGRect imageRect = CGRectMake(10, 100, 80, 80);
CGContextDrawImage(context, imageRect, self.image);

看上去,也非常简单,运行查看结果,发现图片上下颠倒了。因为Quartz图形绘制左下角才是原点,而UIView右下角为原点。

拨乱反正使得图片正确显示

既然是坐标系的问题,那么想当然的就是翻转坐标系。

1
2
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

运行结果发现,图片确实没有上下颠倒了,但是原点确实从左下角开始的。即图片位置并不是你想要的。

那只需要改变y坐标就可以了。

1
2
CGRect imageRect = CGRectMake(10, 100, 80, 80);
imageRect.origin.y = self.bounds.size.height - imageRect.origin.y - imageRect.size.height;

更好的处理方式

1
2
3
4
5
6
7
8
9
10
11
12
void drawImage(CGContextRef context, CGRect rect, CGImageRef image){
CGContextSaveGState(context);
CGContextTranslateCTM(context, rect.origin.x, rect.origin.y);
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, -rect.origin.x, -rect.origin.y);
CGContextDrawImage(context, rect, image);
CGContextRestoreGState(context);
}

使用CGContextSaveGStateCGContextRestoreGState不影响其他的绘制。

平铺

使用CGContextDrawTiledImage平铺。

1
2
3
4
5
6
7
8
9
10
11
12
void drawTiledImage(CGContextRef context, CGRect rect, CGImageRef image){
CGContextSaveGState(context);
CGContextTranslateCTM(context, rect.origin.x, rect.origin.y);
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, -rect.origin.x, -rect.origin.y);
CGContextDrawTiledImage(context, rect, image);
CGContextRestoreGState(context);
}

裁剪

超过裁剪区域以久的内容,都会被自动裁剪掉, 当然一定需要先设置裁剪,再绘制,不然是无效的。实际上就是显示的范围只有给定的Rect这么大。

1
CGContextClipToRect(context, CGRectMake(100, 100, 100, 100));

内存管理

因为CGImage使用了CGImageRetain,那么CGImage也需要手动释放

1
2
3
- (void)dealloc {
CGImageRelease(_image);
}

文字

使用UIKit提供的绘制文字

drawAtPoint

不会换行。

1
2
NSString *title = @"Hello Quartz.";
[title drawAtPoint:CGPointZero withAttributes:@{}];

drawInRect

会自动换行。

1
[title drawInRect:CGRectMake(0, 0, 100, 50) withAttributes:@{}];

字体大小

刚刚绘制的时候属性参数都传递的是一个空字典,要想修改字体大小就通过传递一个字典参数。

1
2
3
NSDictionary *attributes = @{
NSFontAttributeName: [UIFont systemFontOfSize:30]
};

字体颜色

让字体颜色变成红色。添加到attributes 属性字典中。

1
NSForegroundColorAttributeName: [UIColor redColor]

描边

NSStrokeColorAttributeName需要与NSStrokeWidthAttributeName配合使用。

1
2
NSStrokeColorAttributeName: [UIColor blueColor],
NSStrokeWidthAttributeName: @2

更多字体属性

更多的字体属性设置参考NSAttributedString头文件。里面有阴影、字间距、删除线、下划线等。

关于Quartz绘制字体

CGContextShowTextAtPoint 这些绘制文字的API已经过时了,被CoreText API替换。

参考

  1. Quartz 2D Programming Guide
  2. Quartz 2D Programming Guide 中文

源代码

Quartz2D

坚持原创技术分享,您的支持将鼓励我继续创作!