quartz2d(一)

转载自:http://m.2cto.com/kf/201612/572489.html

 

UIKIt UIBezierPath Core Graphics OpenGL ES Quartz2D的区别和联系

UIKIt:UIKit中的控件都是基于Core Graphics实现的

UIBezierPath:UIBezierPath属于UIKit,它是苹果对复杂的Core Graphics进行的封装,方便我们用OC语言进行简单的绘图

Core Graphics:是一套基于C语言的API,支持向量图形,线、形状、图案、路径、剃度、位图图像和pdf 内容的绘制。

OpenGL ES:OpenGL是由SGI公司开发的一套3D图形软件接口标准,它只是一个标准,具体实现由机械制造商来完成,所以不同的机器他的效果可能是完全不一样的。OpenGL-ES版本,主要是应对嵌入式环境和应用的要求,应该说在高效完成2D/3D界面的同时,达到了降低功耗的效果。

Quartz2D:quartz是一个通用的术语,用于描述在IOS整个媒体层用到的多种技术 包括图形、动画、音频、适配。Quart 2D 是一组二位绘图和渲染API,Core Graphic会使用到这组API

Core Graphics框架

绘图我们要用到Core Graphics框架,Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎。该框架可以用于基于路径的绘图、变换、颜色管理、脱屏渲染,模板、渐变、遮蔽、图像数据管理、图像的创建、遮罩以及PDF文档的创建、显示和分析。

Core Graphics API所有的操作都在一个上下文中进行。所以在绘图之前需要获取该上下文并传入执行渲染的函数中。介绍两种最为常用的获取方法。

第一种方法就是创建一个图片类型的上下文。调用UIGraphicsBeginImageContextWithOptions函数就可获得用来处理图片的图形上下文。利用该上下文,你就可以在其上进行绘图,并生成图片。调用UIGraphicsGetImageFromCurrentImageContext函数可从当前上下文中获取一个UIImage对象。记住在你所有的绘图操作后别忘了调用UIGraphicsEndImageContext函数关闭图形上下文。

第二种方法是利用cocoa为你生成的图形上下文。当你子类化了一个UIView并实现了自己的drawRect:方法后,一旦drawRect:方法被调用,Cocoa就会为你创建一个图形上下文,此时你对图形上下文的所有绘图操作都会显示在UIView上。

什么是Quartz2D绘图引擎

Quartz 2D是?个二维绘图引擎,iOS中?部分控件的内容都是通过Quartz 2D画出来的。 Quartz 2D是纯C语言的 Quartz 2D的API来自于CoreGraphics框架 数据类型和函数基本都是CG作为前缀
CGContextRef CGPathRef CGContextStrokePath(ctx)

Quartz 2D能完成的工作

绘制图形 : 线条\三角形\矩形\圆\弧形 绘制文字 绘制\生成图片(图像) 读取\生成PDF 截图\裁剪图片 自定义UI控件

Quartz2D绘图有以下两种方式

注意: UIBezierPath 对象可以独立使用, 无需手动获取“图形上下文”对象,此处为了更好的理解“图形上下文对象”所以暂时还是采用手动获取“图形上下文”对象的方式来绘图。<�喎�"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMiBpZD0="drawrect方法的使用">drawRect:方法的使用View内部有个layer(图层)属性,drawRect:方法中取得的是一个Layer Graphics Context,因此,绘制的东西其实是绘制到view的layer上去了 iOS的绘图操作是在UIView类的drawRect方法中完成的,重写drawRect方法,在这里进行绘图操作,程序会自动调用此方法进行绘图。 重绘操作仍然在drawRect方法中完成,苹果要求我们调用UIView类中的setNeedsDisplay方法,则程序会自动调用drawRect方法进行重绘。(调用setNeedsDisplay会自动调用drawRect)。 在UIView中,重写drawRect: (CGRect) aRect方法,可以自己定义想要画的图案.且此方法一般情况下只会画一次.也就是说这个drawRect方法一般情况下只会被掉用一次. 当某些情况下想要手动重画这个View,只需要掉用[self setNeedsDisplay]方法即可. rect指的就是当前view的bounds,即当前视图的所有范围。 drawRect:方法是系统帮我们调用的,不能手动去调用这个方法。原因是手动去调用drawRect方法的时候无法保证系统已经帮我们创建好了”图形上下文”,所以这样就无法保证在drawRect:方法中获取”图形上下文”对象,也就无法进行绘图了。 当这个view第一次显示的时候会调用一次drawRect方法;
当这个view执行重绘操作的时候会重新调用drawRect方法;
通过调用[self setNeedsDisplay] 【重绘整个view】或[self setNeedsDisplayInRect:]【重绘指定区域】的方式让view执行drawRect方法进行重绘。

图形上下文CGContextRef

图形上下文的本质:是一个Quartz2D的绘图环境!

几种不同的渲染方式



c语言的api绘制基本图形">通过C语言的API绘制基本图形

#pragma mark - 通过C语言的API绘制基本图形
- (void)drawRect:(CGRect)rect {

    // 1.获取上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    // 2.设置起点
    CGContextMoveToPoint(cxtRef, 50, 50);
    // 3.添加线
    CGContextAddLineToPoint(cxtRef, 150, 150);
    // 3.2 继续加线段
    CGContextAddLineToPoint(cxtRef, 250, 50);

    // 3.3 向下的线
    // 注意:如果想要新开线,需要重新设置起点! 
    CGContextMoveToPoint(cxtRef, 150, 150);
    CGContextAddLineToPoint(cxtRef, 150, 250);

    // 4.渲染
//    kCGPathFill 将线段包含的部分渲染成黑色!
//    kCGPathStroke 只负责绘制线条!
    CGContextDrawPath(cxtRef, kCGPathStroke);
}

UIBezierPath进行直线图形绘制

#pragma mark - 通过UIBezierPath进行图形绘制
// 不用写关于图形上下文的代码,已经封装了,但是本质,还是需要获取到view相关的图形上下文,进行绘制!
- (void)drawRect:(CGRect)rect {

    // 1.获取图形上下文
//    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 2.创建路径
    UIBezierPath *path = [UIBezierPath bezierPath];
    // 3.1 设置起点
    [path moveToPoint:CGPointMake(50, 50)];
    // 3.2 添加线
    [path addLineToPoint:CGPointMake(150, 150)];
    [path addLineToPoint:CGPointMake(250, 50)];

    // 4.渲染--不写图形上下文的代码时,直接使用此方法渲染绘制
    [path stroke];

    // 4.将路径对象添加到图形上下文
//    CGContextAddPath(cxtRef, path.CGPath);
    // 5.渲染
//    CGContextDrawPath(cxtRef, kCGPathStroke);
}

UIBezierPath绘制矩形

// MARK: - 1.矩形
- (void)drawRect:(CGRect)rect {
    // 1.获取图形上下文
//    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 2.创建矩形的路径
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 200, 80)];
    // 3.渲染
    [path stroke];

    // 3.添加路径到图形上下文
//    CGContextAddPath(cxtRef, path.CGPath);
    // 4.渲染
//    CGContextDrawPath(cxtRef, kCGPathStroke);
}

UIBezierPath绘制圆角矩形

// MARK: - 2.圆角矩形
- (void)drawRect:(CGRect)rect {
//    // 1.获取图形上下文
//    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 2.创建路径
    CGFloat radius = 20;
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 200, 80) cornerRadius:radius];
    // 3.渲染
    [path stroke];

//    // 3.添加路径
//    CGContextAddPath(cxtRef, path.CGPath);
//    // 4.渲染
//    CGContextDrawPath(cxtRef, kCGPathStroke);
}

UIBezierPath绘制椭圆

//    // 1.获取图形上下文
//    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 2.创建椭圆的路径
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 200, 80)];
    // 3.渲染
    [path stroke];

//    // 3.添加
//    CGContextAddPath(cxtRef, path.CGPath);
//    // 4.渲染
//    CGContextDrawPath(cxtRef, kCGPathStroke);

UIBezierPath绘制弧形

- (void)drawRect:(CGRect)rect {

    // MARK: - 4.弧形
//    // 1.获取上下文
//    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 2.圆弧路径
    /**
     center 圆心
     radius 半径
     startAngle 起始角度
     endAngle 结束角度
     clockwise 是否为顺时针!
     */
    // 圆心
    CGPoint center = CGPointMake(150, 150);
    // 半径
    CGFloat radius = 100;
    // 起始角度
    CGFloat startA = 0;
    // 结束角度
    CGFloat endA = M_PI_2;

    // 是否为顺时针
    BOOL clockwise = NO;

    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:clockwise];

    // 3.渲染
    [path stroke];

//    // 3.添加路径
//    CGContextAddPath(cxtRef, path.CGPath);
//    // 4.渲染
//    CGContextDrawPath(cxtRef, kCGPathStroke);
}

UIBezierPath绘制圆形

// 2.圆路径
    /**
     center 圆心
     radius 半径
     startAngle 起始角度
     endAngle 结束角度
     clockwise 是否为顺时针!
     */
    // 圆心
    CGPoint center = CGPointMake(150, 150);
    // 半径
    CGFloat radius = 100;
    // 起始角度
    CGFloat startA = 0;
    // 结束角度
    CGFloat endA = M_PI * 2;

    // 是否为顺时针
    BOOL clockwise = NO;

    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:clockwise];

    // 渲染
    [path stroke];

UIBezierPath绘制扇形

// 2.圆弧路径
    /**
     center 圆心
     radius 半径
     startAngle 起始角度
     endAngle 结束角度
     clockwise 是否为顺时针!
     */
    // 圆心
    CGPoint center = CGPointMake(150, 150);
    // 半径
    CGFloat radius = 100;
    // 起始角度
    CGFloat startA = 0;
    // 结束角度
    CGFloat endA = M_PI_4;

    // 是否为顺时针
    BOOL clockwise = YES;

    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:clockwise];

    // 2.2 向圆心添加线
    [path addLineToPoint:center];

    // 渲染
    [path fill];

绘图状态信息设置

1> 设置线宽 CGContextSetLineWidth
2> 设置线头样式 CGContextSetLineCap
3> 设置线段接头样式 CGContextSetLineJoin
4> 颜色的设置 [[UIColor redColor] set]

- (void)drawRect:(CGRect)rect {

    //    // 1.获取图形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 2.创建矩形的路径
    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:CGPointMake(50, 50)];
    // 第一根线
    [path addLineToPoint:CGPointMake(150, 150)];
    // 第二根线
    [path addLineToPoint:CGPointMake(250, 50)];

    // 3.添加路径到图形上下文
    CGContextAddPath(cxtRef, path.CGPath);

    // 3.1 设置线宽!
    CGContextSetLineWidth(cxtRef, 20);
    // 3.2 设置线头样式
    /**
     kCGLineCapButt,   比较难看的!
     kCGLineCapRound,  圆角
     kCGLineCapSquare  平角
     */
    CGContextSetLineCap(cxtRef, kCGLineCapButt);

    // 3.3 设置接头的样式
    /**
     kCGLineJoinMiter,  尖头
     kCGLineJoinRound,  圆角
     kCGLineJoinBevel   多余的部分感觉被切了!
     */
    CGContextSetLineJoin(cxtRef, kCGLineJoinMiter);

    // 3.4 颜色的设置
    [[UIColor redColor] setStroke];
    [[UIColor yellowColor] setFill];

    // 4.渲染
    CGContextDrawPath(cxtRef, kCGPathFillStroke);
}

两种填充原则

- (void)drawRect:(CGRect)rect {

#pragma mark - 非零绕数填充规则
    [self nonzeroWindingNumberRule];

#pragma mark - 奇偶填充规则
    [self evenOddRule];
}

#pragma mark - 非零绕数填充规则
    /**
     * 非零绕数填充规则:
     *      在图形上下文中的任何一个点
     *      被顺时针覆盖标记为 1
     *      被逆时针覆盖标记为 -1
     *      当标记为0的时候不填充,其他则填充
     * 这个规则与方向有关,与次数无关
     */
- (void)nonzeroWindingNumberRule {

    // 1.获取图形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 2.绘制顺时针的圆形
    UIBezierPath *arcY = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:100 startAngle:0 endAngle:M_PI * 2 clockwise:YES];

    // 3.绘制逆时针的圆形
    UIBezierPath *arcN = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:75 startAngle:0 endAngle:M_PI * 2 clockwise:NO];

    // 验证与次数无关
    UIBezierPath *arc3 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 230) radius:50 startAngle:0 endAngle:M_PI * 2 clockwise:NO];

    // 4.添加路径对象
    CGContextAddPath(cxtRef, arcY.CGPath);
    CGContextAddPath(cxtRef, arcN.CGPath);
    CGContextAddPath(cxtRef, arc3.CGPath);

    [[UIColor yellowColor] setFill];

    // 5.渲染
    CGContextDrawPath(cxtRef, kCGPathFill);

}

#pragma mark - 奇偶填充规则
// even:偶数  odd:偶数
/**
 * 奇偶填充规则解释:
 *      在图形上下文中的任何一个点
 *      如果它被路径对象覆盖了奇数次,在进行渲染操作的时候这个点就会被渲染
 *      如果它被路径对象覆盖了偶数次,在进行渲染操作的时候这个点就不会被渲染
 */
- (void)evenOddRule {

    // 获取图形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 绘制水平方向矩形
    UIBezierPath *rectH = [UIBezierPath bezierPathWithRect:CGRectMake(50, 100, 200, 100)];

    // 绘制圆形
    UIBezierPath *arcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:75 startAngle:0 endAngle:M_PI * 2 clockwise:YES];

    // 绘制竖直方向的矩形
    UIBezierPath *rectV = [UIBezierPath bezierPathWithRect:CGRectMake(180, 25, 50, 250)];

    // 将路径对象添加到图形上下文中
    CGContextAddPath(cxtRef, rectH.CGPath);
    CGContextAddPath(cxtRef, arcPath.CGPath);
    CGContextAddPath(cxtRef, rectV.CGPath);

    [[UIColor orangeColor] setFill];

    // 同时显示stroke和fill的颜色
    CGContextDrawPath(cxtRef, kCGPathEOFill);
}

模拟下载进度条

思路:
在控制器中将 slider 的值传递给自定义 view
在自定义 View中,根据传递过来的值绘制弧。
创建一个与自定义 view 一样大小的 label 来显示下载进度

#import 

@interface DownloadView : UIView
/**
 *  接收滑块的值
 */
@property (nonatomic, assign) float sliderValue;
@end
#import "DownloadView.h"

@interface DownloadView ()

// 需要先写,再拖线!
/**
 *  显示百分比的label
 */
@property (nonatomic, weak) IBOutlet UILabel *valueLbl;

@end

@implementation DownloadView

#pragma mark - 重写set方法,执行重绘操作
- (void)setSliderValue:(float)sliderValue {
    _sliderValue = sliderValue;
    // 内部执行重绘操作
    [self setNeedsDisplay];
    self.valueLbl.text = [NSString stringWithFormat:@"%.2f%%", (sliderValue * 100)];
}

#pragma mark - 绘制下载进度条
- (void)drawRect:(CGRect)rect {
    // 1.获取上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();
    // 2.创建路径
    CGFloat startA = -M_PI_2;
    CGFloat endA = self.sliderValue * M_PI * 2 - M_PI_2;

    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:150 startAngle:startA endAngle:endA clockwise:YES];

    // 保证绘制出来是扇形
    [path addLineToPoint:CGPointMake(150, 150)];
    // 3.添加路径
    CGContextAddPath(cxtRef, path.CGPath);
    // 4.渲染
    CGContextDrawPath(cxtRef, kCGPathFill);
}

@end
#import "ViewController.h"
#import "DownloadView.h"

@interface ViewController ()

/**
 *  下载进度的视图
 */
@property (nonatomic, weak) IBOutlet DownloadView *downloadView;

@end

@implementation ViewController

#pragma mark - 滑动事件
- (IBAction)sliderValueChanged:(UISlider *)sender {
    NSLog(@"现在的值是 %f", sender.value);
    // 传递值给自定义view
    self.downloadView.sliderValue = sender.value;
}

@end

饼状图

思路:
构建数据,NSArray *data = @[@30, @15, @5, @17, @3, @10, @20];。
根据数据个数绘制“扇形”(弧)
注意:
每个弧的起始、结束弧度都是不一样的
每次绘制完毕一个弧以后都要重新设置下一次的起始弧度为当前的结束弧度
本次绘制的结束弧度,为起始弧度+本次的弧度

- (void)drawRect:(CGRect)rect {

    // 1.数据
    NSArray *data = @[@30, @15, @5, @17, @3, @10, @20];

    // 1.2 获取图形上下文
    CGContextRef cxtRef = UIGraphicsGetCurrentContext();

    // 2.遍历
    // 2.0.1 圆心,半径
    CGPoint center = CGPointMake(150, 150);
    CGFloat radius = 120;
    BOOL clockwise = YES;

    // 2.0.2 定义起始角度的变量
    CGFloat startA = 0;

    for (NSInteger i = 0; i < data.count; i++) {

        // 2.1 取出数据,并转为int类型
        float number = [data[i] floatValue];

        // 2.2 计算所占用的角度
        CGFloat AngleOfNum = number / 100 * (M_PI * 2);

        // 2.3 计算结束角度
        CGFloat endA = startA + AngleOfNum;

        // 2.4 创建路径对象
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:clockwise];

        // 2.4.2 添加线
        [path addLineToPoint:center];

        // 2.5 添加路径
        CGContextAddPath(cxtRef, path.CGPath);

        // 随机色
        [[UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0] setFill];

        // 2.6 渲染
        CGContextDrawPath(cxtRef, kCGPathFill);

        // 2.7 改变角度
        startA = endA;
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容