1.什么是Quartz2D:
是一个二维绘图引擎,同时支持iOS和Mac OS系统(跨平台,C语言写的),包含在
core graphICS 框架中,
2.Quartz2D能干什么?
绘制图形:线条,三角形,矩形,圆,弧等
绘制文字
绘制/生成图片
读取生成PDF
截图/裁剪图片
自定义UI控件
3.注意:
Quartz2D是苹果官方的二维绘图引擎,同时支持iOS和Mac OS系统,
Cocos2D(Cocos2D-X,Cocos2D-iphone,Cocos2D-html等)是一个第三方开源的2D游戏框架,做2D游戏的还有Spite Kit,一般3D游戏用unity3D
4.Quartz2D须知:
Quartz2D的API是C语言写的
Quartz2D的API来自Core Graphic框架
数据类型和函数基本都以CG作为前缀
CG类和ontextRef
CGPathRef
CGContextStrokePath(ctx)
...
5.Quartz2D绘图主要步骤:
1.获取[图形上下文]对象
2.向[图形上下文] 对象中添加[路径]
3.渲染(把[图形上下文]中的图形绘制到对应的设备上)
4.图形上下文CGContextRef:
图形上下文中主要包含如下信息:
1.绘图路径(各种各样的图形)
2.绘图状态(颜色,线宽,样式,旋转,缩放,平移,图片裁剪区域)
3.输出目标(绘制到什么地方去,UIView,图片,PDF,打印机等)
(输出目标可以是PDF文件,bitmap或者显示器的窗口上)
要绘制的图形 -保存->图形上下文-显示->输出目标
相同的一套绘图序列,制定不同的Graphics Context,就可以将相同的图像绘制到不同目标上.
图形上下文对象:
graphICS context:
Bitmap graphICS context
PDF graphICS context
Window graphICS context
layer graphICS context(UIView控件)
Printer graphICS context
使用Quartz2D绘图:
方式1:直接条用Quartz2D的API进行绘制
代码量稍大
功能全面
步骤:
获取绘图上下文
把图形绘制到图形上下文上
把图形上下文渲染到相应设备上
方式2:调用UIKit 框架封装好的API进行绘图
代码相对简单
只对部分Quartz2D API做了封装
对于没有封装的功能只能调用Quartz2D 原生的API
比如:画图片,文字到控件上(UIKit已经封装好了)
6.绘图的方式:
1.使用C语言:
#import "myUIView.h"
@implementation myUIView
- (void)drawRect:(CGRect)rect{
//1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.拼接路径,同时把路径添加到上下文中
CGContextMoveToPoint(ctx, 10, 100);//移动到点
CGContextAddLineToPoint(ctx, 20, 100);//画线到点
//3.渲染
CGContextStrokePath(ctx);
}
2.使用C语言+oc语言或swift:
#import "myUIView.h"
@implementation myUIView
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if(self){
self.backgroundColor = [UIColor blueColor];
return self;
}
return nil;
}
- (void)drawRect:(CGRect)rect{
//1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.拼接路径
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(0, 0)];
[path addLineToPoint:CGPointMake(self.bounds.size.width, self.bounds.size.height)];
[path addLineToPoint:CGPointMake(self.bounds.size.width, 0)];
[path addLineToPoint:CGPointMake(0, self.bounds.size.height)];
//3.把路径添加到上下文中
CGContextAddPath(ctx, path.CGPath);
//4.渲染
CGContextStrokePath(ctx);
}
@end
3.使用oc语言或swift语言:
- (void)drawRect:(CGRect)rect{
//1.创建路径
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(0, 0)];\
[path addLineToPoint:CGPointMake(self.bounds.size.width, self.bounds.size.height)];
//2.渲染
[path stroke];
}
7.drawRect:
1.绘图代码为什么要写在drawRect方法中?
因为在这个方法中可以获取到正确的上下文
2.drawRect的参数(CGRect)rect含义:
rect是当前view的Bounds(在窗口中得位置)
3.drawRect方法什么时候调用:
1.当这个view第一次显示的时候
2.重绘(相当于TableView的刷新)的时候会调用
4.如何进行重绘:
需要调用需要重绘view的 setNeedsDisplay方法
需要调用需要重绘view的 setNeedsDisplayInRect方法,Rect 制定刷新的区域
5.为什么不能手动调用drawRect方法:
因为要获取到正确的绘图上下文,手动调用的话,系统可能还没创建好上下文
8.绘图练习:
1.绘制线段:
- (void)drawRect:(CGRect)rect{
//1.创建路径
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(10, 10)];
[path addLineToPoint:CGPointMake(50, 50)];
UIBezierPath *path1 = [[UIBezierPath alloc] init];
[path1 moveToPoint:CGPointMake(30, 10)];
[path1 addLineToPoint:CGPointMake(70, 80)];
//2.渲染
[path stroke];
[path1 stroke];
}
2.画矩形:
- (void)drawRect:(CGRect)rect{
//1.创建路径
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 50, 50)];
//2.渲染
[path stroke];
}
3.画圆角矩形:
- (void)drawRect:(CGRect)rect{
//1.创建路径
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10, 10, 50, 50) cornerRadius:15];
//2.渲染
[path stroke];
}
4.画椭圆(当宽度和高度一样的时候是圆):
- (void)drawRect:(CGRect)rect{
//1.创建路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 30, 40)];
//2.渲染
[path stroke];
}
5.画弧:
- (void)drawRect:(CGRect)rect{
//1.创建路径
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50) radius:20 startAngle:M_PI / 3 endAngle:M_PI / 2 clockwise:YES];//参数:圆心,半径,开始位置,结束位置,是否顺时针
//2.渲染
[path stroke];
}
6.画圆环:
- (void)drawRect:(CGRect)rect{
//1.创建路径
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50) radius:20 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50) radius:10 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
//2.渲染
[path1 stroke];
[path stroke];
}
9.绘图的样式:
1.设置线宽:
C语言:
- (void)drawRect:(CGRect)rect{
//1.创建路径
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextMoveToPoint( ctx, 10, 10);
CGContextAddLineToPoint(ctx, 50, 50);
CGContextAddLineToPoint(ctx, 20, 10);
//设置线条宽度
CGContextSetLineJoin(ctx, kCGLineJoinBevel);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextSetLineWidth(ctx, 10);
//设置颜色
CGContextSetRGBStrokeColor(ctx, 0, 1, 1, 1);
CGContextStrokePath(ctx);
}
oc语言或swift:
- (void)drawRect:(CGRect)rect{
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(10, 20)];
[path addLineToPoint:CGPointMake(50, 60)];
[path addLineToPoint:CGPointMake(10, 60)];
[path closePath];//关闭路径
[path setLineWidth:5];//设置线宽度
[path setLineCapStyle:kCGLineCapButt];//设置开始和结尾的样式
[path setLineJoinStyle:kCGLineJoinBevel];//设置连接处的 样式
//设置颜色
[[UIColor redColor] setStroke];
[path stroke];
}
- (void)drawRect:(CGRect)rect{
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(10, 20)];
[path addLineToPoint:CGPointMake(50, 60)];
[path addLineToPoint:CGPointMake(10, 60)];
// [path addLineToPoint:CGPointMake(10, 20)];
[path closePath];//关闭路径
[path setLineWidth:2];//设置线宽度
[path setLineCapStyle:kCGLineCapButt];//设置开始和结尾的样式
[path setLineJoinStyle:kCGLineJoinBevel];//设置连接处的 样式
//设置颜色线条
[[UIColor redColor] setStroke];
//设置填充
// [path fill];
[[UIColor yellowColor] setFill];//设置填充颜色
[path fill];
[path stroke];
}
奇偶填充规则:
当有两个或以上的图形叠加的时候,叠加的部分,如果是叠加了偶数次,则不给填充
- (void)drawRect:(CGRect)rect{
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIBezierPath *juxing = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 30, 50)];
UIBezierPath *yuan = [UIBezierPath bezierPathWithArcCenter:CGPointMake(25, 35) radius:15 startAngle:0 endAngle:M_PI* 2 clockwise:YES];
UIBezierPath *juxing1 = [UIBezierPath bezierPathWithRect:CGRectMake(5, 5, 50, 50)];
CGContextAddPath(ctx, juxing.CGPath);
CGContextAddPath(ctx, yuan.CGPath);
CGContextAddPath(ctx, juxing1.CGPath);
CGContextDrawPath(ctx, kCGPathEOFill);
}
非0环绕数规则:默认的填充方式,从左到右开跨过,+1.从右到左跨过-1.如果最后为0,那么不填充,否则填充
- (void)drawRect:(CGRect)rect{
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIBezierPath *yuan = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50) radius:50 startAngle:0 endAngle:M_PI* 2 clockwise:YES];
UIBezierPath *yx = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50) radius:30 startAngle:0 endAngle:M_PI * 2 clockwise:NO];
CGContextAddPath(ctx, yuan.CGPath);
CGContextAddPath(ctx, yx.CGPath);
CGContextDrawPath(ctx, kCGPathFill);
}
10.绘制饼状图:
.m文件:
#import <UIKit/UIKit.h>
@interface BingtuUIView : UIView
@property(nonatomic,strong) NSArray *data;
@property(nonatomic,strong) NSArray<UIColor *> *colors;
- (instancetype)initWithFrame:(CGRect)frame andData :(NSArray*) data andColors:(NSArray<UIColor*>*) colors;
@end
.h文件:
#import "BingtuUIView.h"
@implementation BingtuUIView
- (instancetype)initWithFrame:(CGRect)frame andData :(NSArray*) data andColors:(NSArray<UIColor*>*) colors{
if(frame.size.height != frame.size.width){
//如果控件的高度和宽度不一样,则抛出异常
[NSException raise:@"饼状图的宽度和高度必须一样" format:@"不一样的话,显示不好看"];
}
self = [super initWithFrame:frame];
if (self) {
self.data = data;
self.colors = colors;
return self;
}
return nil;
}
//模拟数据
- (NSArray *)data{
if (!_data) {
_data = @[@30,@20,@20,@30];
}
return _data;
}
- (NSArray<UIColor *> *)colors{
if (!_colors) {
_colors = [NSArray arrayWithObjects:[UIColor redColor],[UIColor whiteColor],[UIColor blueColor],[UIColor orangeColor], nil];
}
return _colors;
}
- (void)drawRect:(CGRect)rect{
int sum = 0;
for (id num in self.data) {
sum += [num floatValue];
}
float star = 0,end= 0;
for (int i = 0; i< self.data.count;i++) {
end = 2 * M_PI * ([self.data[i] floatValue] / sum) + star;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2) radius: (self.bounds.size.height < self.bounds.size.width ? self.bounds.size.height:self.bounds.size.width) / 2 startAngle:star endAngle:end clockwise:YES];
[path addLineToPoint:CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2)];
[self.colors[i] setFill];
[path fill];
star = end;
}
}
@end
使用:
- (BingtuUIView *)bing{
if (!_bing) {
_bing = [[BingtuUIView alloc] initWithFrame:CGRectMake(75, 75, 150 , 150) andData:@[@10,@38,@40,@30] andColors:@[[UIColor redColor],[UIColor blueColor],[UIColor orangeColor],[UIColor whiteColor]]];
_bing.backgroundColor = [UIColor yellowColor];
}
return _bing;
}
等学会了画字符串再把字符串加上.
11.绘制柱状图:
.h文件:
#import <UIKit/UIKit.h>
@interface ZhuTuUIView : UIView
@property(nonatomic,strong) NSArray *data;
@property(nonatomic,strong) NSArray<UIColor*> *colors;
- (instancetype)initWithFrame:(CGRect)frame andData:(NSArray *)data andColors:(NSArray<UIColor*>*) colors;
@end
.m文件:
@implementation ZhuTuUIView
- (instancetype)initWithFrame:(CGRect)frame andData:(NSArray *)data andColors:(NSArray<UIColor *> *)colors{
if(data.count != colors.count){
[NSException raise:@"" format:@""];
}
self = [super initWithFrame:frame];
if (self) {
self.data = data;
self.colors = colors;
return self;
}
return nil;
}
- (void)drawRect:(CGRect)rect{
//计算出data中的最大数
float max = [self.data[0] floatValue];
for (id num in self.data) {
float numfloat = [num floatValue];
max = max > numfloat ? max:numfloat;
}
float kdu = self.bounds.size.width / (self.data.count * 2 - 1);//100/8=12.5
float value = self.bounds.size.height / max;//100/40 = 2.5
for (int i = 0; i < self.data.count; i++) {
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake((i - 1) * 2 * kdu + 2 * kdu , self.bounds.size.height - [self.data[i] floatValue] * value + 20, kdu, [self.data[i] floatValue] * value)];
[self.colors[i] setFill];
[path fill];
}
}
@end
使用:
- (ZhuTuUIView *)zhuTu{
if (!_zhuTu) {
_zhuTu = [[ZhuTuUIView alloc] initWithFrame:CGRectMake(75, 75, 150 , 100) andData:@[@20,@25,@28,@30,@29,@50] andColors:@[[UIColor redColor],[UIColor blueColor],[UIColor orangeColor],[UIColor whiteColor],[UIColor brownColor],[UIColor blackColor]]];
_zhuTu.backgroundColor = [UIColor yellowColor];
}
return _zhuTu;
}
12.自定义进度条:
.h文件:
#import <UIKit/UIKit.h>
@interface LoadingUIView : UIView
@property(nonatomic,assign) float value;
@property(nonatomic,assign) float maxValue;
@property(nonatomic,strong) UIColor *color;
- (instancetype)initWithFrame:(CGRect)frame addVale:(float) value addMaxVale:(float) maxValue addColor :(UIColor *) color;
@end
.m文件:
#import "LoadingUIView.h"
@interface LoadingUIView ()
@property(nonatomic,strong) UILabel *label;
@end
@implementation LoadingUIView
- (UILabel *)label{
if(!_label){
_label = [[UILabel alloc] initWithFrame:CGRectMake(self.bounds.size.width / 2 - 20, self.bounds.size.height / 2 - 10, 40, 20)];
//_label.backgroundColor = [UIColor colorWithRed:1 green:0 blue:1 alpha:1];
[_label setTextColor:[UIColor blueColor]];
}
return _label;
}
- (instancetype)initWithFrame:(CGRect)frame addVale:(float)value addMaxVale:(float) maxValue addColor:(UIColor *)color{
self = [super initWithFrame:frame];
if(self){
self.value = value;
self.color = color;
self.maxValue = maxValue;
[self addSubview:self.label];
return self;
}
return nil;
}
- (void)drawRect:(CGRect)rect{
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2) radius:(self.bounds.size.height < self.bounds.size.width ? self.bounds.size.height:self.bounds.size.width) / 2 startAngle:0 endAngle: 2 * M_PI * (self.value / self.maxValue) clockwise:YES];
[path addLineToPoint:CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2)];
self.label.text = [NSString stringWithFormat:@"%0.0lf%%",self.value * 100 / self.maxValue];
[self.color setFill];
[path fill];
}
@end
使用:
- (LoadingUIView *)loading{
if (!_loading) {
_loading = [[LoadingUIView alloc]initWithFrame:CGRectMake(75, 75, 150 , 150) addVale: 0 addMaxVale:100 addColor:[UIColor redColor]];
_loading.backgroundColor = [UIColor yellowColor];
}
return _loading;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.loading];
UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(self.view.bounds.size.width / 6, 300, self.view.bounds.size.width / 3 * 2, 10)];
slider.value = 0;
slider.maximumValue = 100;
slider.minimumValue = 0;
[slider addTarget:self action:@selector(sliderChage:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:slider];
}
- (void) sliderChage:(UISlider*)slider{
float value = slider.value;
self.loading.value = value;
[self.loading setNeedsDisplay];
}
13.矩形操作:
其实是对上下文进行操作:
- (void)drawRect:(CGRect)rect{
CGContextRef ctx = UIGraphicsGetCurrentContext();
//旋转
CGContextRotateCTM(ctx, M_PI_4 / 10);
//缩放
CGContextScaleCTM(ctx, 0.5, 0.5);
//平移
CGContextTranslateCTM(ctx, 20, 60);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 50, 80)];
[[UIColor redColor] setFill];
[path fill];
UIBezierPath *path1 = [[UIBezierPath alloc]init];
[path1 moveToPoint:CGPointMake(0, 0)];
[path1 addLineToPoint:CGPointMake(self.bounds.size.width, self.bounds.size.height)];
[path1 stroke];
}
14.图形上下文栈:
15.quartz2d内存管理
16.绘制文字:
- (void)drawRect:(CGRect)rect{
//文字对象
NSString *str = @"你好,闻品高";
// [str drawAtPoint:CGPointMake(10, 10) withAttributes:nil];
[str drawInRect:CGRectMake(5, 50, 80, 50) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:20],NSForegroundColorAttributeName:[UIColor redColor]}];
}
17.绘制图片:
- (void)drawRect:(CGRect)rect{
UIImage *image = [UIImage imageNamed:@"psb.png"];
//小图的绘制
// [image drawAtPoint:CGPointMake(0, 0)];//从某个点开始绘制
// [image drawInRect:rect];//将图片绘制到某个区域,拉伸的
// [image drawAsPatternInRect:rect];//将图片绘制到某个区域,平铺
//大图的绘制
[image drawAsPatternInRect:rect];
}
18.裁剪上下文的显示区域:
- (void)drawRect:(CGRect)rect{
UIImage *image = [UIImage imageNamed:@"psb.png"];
CGContextAddArc(UIGraphicsGetCurrentContext(), self.bounds.size.width / 2, self.bounds.size.height / 2, (self.bounds.size.width > self.bounds.size.height ? self.bounds.size.height : self.bounds.size.width) / 2, 0, 2* M_PI, 1);//限定裁剪的区域
CGContextClip(UIGraphicsGetCurrentContext());//裁剪
[image drawInRect:rect];
}
19.Bitmap上下文:
1.开启图片的图形上下文
2.绘制简单图形
3.关闭图片的图形上下文
4.保存到沙盒中
1.把图片转为NSDataduix
2.调用data的writertofile方法
//声明属性
@property(nonatomic,strong) UIImageView *imageView;
//加载
- (UIImageView *)imageView{
if (!_imageView) {
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"psb"]];
_imageView.frame = self.view.bounds;
}
return _imageView;
}
//添加到view上
[self.view addSubview:self.imageView];
//点击屏幕时,发生
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//开启一个图片类型的上下文
// UIGraphicsBeginImageContext(CGSizeMake(200, 200));
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 1);
//获取当前上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor redColor] setStroke];
CGContextAddArc(ctx, 100, 100, 100, 0, 2 * M_PI, 1);//画圆
CGContextStrokePath(ctx);//渲染
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();//输出到图片
UIGraphicsEndImageContext();//关闭图片图形上下文
self.imageView.image = img;//添加到界面
//获取doc目录
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES)[0];
NSString *file = [path stringByAppendingString:@"1.jpg"];
//保存到沙盒
NSData * data = UIImageJPEGRepresentation(img, 1);
[data writeToFile:file atomically:YES];
}