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];
}