iOS中 CoreGraphics快速绘图(详解) 韩俊强的博客

第一步:先科普一下基础知识:

Core Graphics是基于C的API,可以用于一切绘图操作

Core Graphics 和Quartz 2D的区别

quartz是一个通用的术语,用于描述在IOS和MAC OS X ZHONG 整个媒体层用到的多种技术 包括图形、动画、音频、适配。

Quart 2D 是一组二位绘图和渲染API,Core Graphic会使用到这组API

Quartz Core 专指Core Animation用到的动画相关的库、API和类

点和像素的对比

系统拥有坐标系,如320*480 硬件有retain屏幕和非retain屏:如320*480、640*960

Core Graphics 使用的是系统的坐标系来绘制图片。在分辨率为640*960手机上绘制图片时,实际上Core Graphics 的坐标是320*480。这个时候每个坐标系上的点,实际上拥有两个像素。

图形上下文

Core Graphics 使用图形上下文进行工作,这个上下文的作用像画家的画布一样。

在图形上下文之外是无法绘图的,我们可以自己创建一个上下文,但是性能和内存的使用上,效率是非常低得。

我们可以通过派生一个UIView的子类,获得它的上下文。在UIView中调用drawRect:方法时,会自动准备好一个图形上下文,可以通过调用

UIGraphicsGetCurrentContext()来获取。 因为它是运行期间绘制图片,我们可以动态的做一些额外的操作

Core Graphics的优点

快速、高效,减小应用的文件大小。同时可以自由地使用动态的、高质量的图形图像。 使用Core Graphics,可以创建直线、路径、渐变、文字与图像等内容,并可以做变形处理。

绘制自定义视图

drawRect:是系统的方法,不要从代码里面直接调用 drawRect:,而应该使用setNeedsDisplay重绘.

需要知道的术语

  • 路径 path
  • 阴影 shadow
  • 笔画 stroke
  • 剪裁路径 Clip Path
  • 线条粗细 Line Width
  • 混合模式 Blend Mode
  • 填充色 Fill Color
  • 当前形变矩阵 Current Transform Matrix
  • 线条图案 Line Dash

图形上下文

一个图形上下文好比是画布上的一副扁平的图画 执行绘画动作,这些动作是在同一个图层上完成的。 图形上下文不允许将内容分不到多个图层中,如果有需求在不同图层上画,可以考虑使用视图层次结构,创建多个UIView,并将他们作为父视图的子视图

图形上下文栈可以把图形上下文的当前状态保存下来,并在执行一些动作后再次恢复回来

CGContextSaveGState();

CGContextStoreGState();

路径、渐变、文字和图像

1. 使用UIBezierPath创建路径

2. 手动创建路径 moveToPoint addLineToPoint addArcWithCenter addCurveToPoint

渐变,渐变可以在指定方向上,以可变的比率在一系列颜色之间转化

线性渐变:沿着一条定义好了起点和重点的直线方向,呈线性变化。如果这条线有一定角度,线性渐变也会沿相同路径变化

放射渐变:颜色顺着两个原型之间的方向线性变化,这两个园为起始圆和终止圆,每隔圆都有自己的圆心和班级

文字

darwAtPoint

drawInRect

图像

Core Graphics 不会保持图像的长宽比例,Core Graphics会将图像的边界设置为CGrect,不管图片是否变形 darwAtPoint drawInRect

第二步:代码部分:

基础画法就不多讲啦!都通用:

第一种绘图形式:在UIView的子类方法drawRect:中绘制一个蓝色圆,使用UIKit在Cocoa为我们提供的当前上下文中完成绘图任务。

 

    - (void) drawRect: (CGRect) rect { 

    UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)]; 

    [[UIColor blueColor] setFill]; 

    [p fill]; 

    } 

 

第二种绘图形式:使用Core Graphics实现绘制蓝色圆。

 

    - (void) drawRect: (CGRect) rect { 

    CGContextRef con = UIGraphicsGetCurrentContext(); 

    CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); 

    CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor); 

    CGContextFillPath(con); 

    } 

 

第三种绘图形式:我将在UIView子类的drawLayer:inContext:方法中实现绘图任务。drawLayer:inContext:方法是一个绘制图层内容的代理方法。为了能够调用drawLayer:inContext:方法,我们需要设定图层的代理对象。但要注意,不应该将UIView对象设置为显示层的委托对象,这是因为UIView对象已经是隐式层的代理对象,再将它设置为另一个层的委托对象就会出问题。轻量级的做法是:编写负责绘图形的代理类。在MyView.h文件中声明如下代码:

 

    @interface MyLayerDelegate : NSObject 

    @end 

 

然后MyView.m文件中实现接口代码:

 

    @implementation MyLayerDelegate 

    - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx { 

      UIGraphicsPushContext(ctx); 

      UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)]; 

      [[UIColor blueColor] setFill]; 

      [p fill]; 

      UIGraphicsPopContext(); 

    } 

    @end 

 

直接将代理类的实现代码放在MyView.m文件的#import代码的下面,这样感觉好像在使用私有类完成绘图任务(虽然这不是私有类)。需要注意的是,我们所引用的上下文并不是当前上下文,所以为了能够使用UIKit,我们需要将引用的上下文转变成当前上下文。

 

因为图层的代理是assign内存管理策略,那么这里就不能以局部变量的形式创建MyLayerDelegate实例对象赋值给图层代理。这里选择在MyView.m中增加一个实例变量,因为实例变量默认是strong:

 

    @interface MyView () { 

    MyLayerDelegate* _layerDeleagete; 

    } 

    @end 

 

使用该图层代理:

 

    MyView *myView = [[MyView alloc] initWithFrame: CGRectMake(0, 0, 320, 480)]; 

    CALayer *myLayer = [CALayer layer]; 

    _layerDelegate = [[MyLayerDelegate alloc] init]; 

    myLayer.delegate = _layerDelegate; 

    [myView.layer addSublayer:myLayer]; 

    [myView setNeedsDisplay]; // 调用此方法,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对象的代码并不需要等待某些方法被调用后或在UIView的子类中才能去做。

 

第五种绘图形式: 使用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(); 

 

解释一下UIGraphicsBeginImageContextWithOptions函数参数的含义:第一个参数表示所要创建的图片的尺寸;第二个参数用来指定所生成图片的背景是否为不透明,如上我们使用YES而不是NO,则我们得到的图片背景将会是黑色,显然这不是我想要的;第三个参数指定生成图片的缩放因子,这个缩放因子与UIImage的scale属性所指的含义是一致的。传入0则表示让图片的缩放因子根据屏幕的分辨率而变化,所以我们得到的图片不管是在单分辨率还是视网膜屏上看起来都会很好。

 

第六种绘图形式: 使用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();

第三步:实践部分:

第一种:基本图形绘制

/**
 *  什么调用:当你视图第一次显示的时候就会调用
 *  作用:绘图
 *  @param rect = self.bounds
 */
- (void)drawRect:(CGRect)rect
{
    // 1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // 2.拼接路径
    UIBezierPath *path = [UIBezierPath bezierPath];

    CGPoint startP = CGPointMake(10, 125);
    CGPoint endP = CGPointMake(240, 125);
    CGPoint controlP = CGPointMake(125, 0);
    [path moveToPoint:startP];
    [path addQuadCurveToPoint:endP controlPoint:controlP];

    // 3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath);

    // 4.渲染上下文到视图
    CGContextStrokePath(ctx);
}

- (void)drawLine
{
    // 1.获取上下文
    // CGContextRef CG CoreGraphics Ref 引用
    // 目前学的上下文都跟UIGraphics有关,以后想直接获取上下文,直接敲一个UIGraphics
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // 2.设置绘图信息(拼接路径)
    UIBezierPath *path = [UIBezierPath bezierPath];

    // 设置起点
    [path moveToPoint:CGPointMake(10, 10)];

    // 添加一条线到某个点
    [path addLineToPoint:CGPointMake(125, 125)];
    [path addLineToPoint:CGPointMake(240, 10)];
    // 3.把路径添加到上下文
    // 直接把UIKit的路径转换成CoreGraphics,CG开头就能转
    CGContextAddPath(ctx, path.CGPath);

    // 4.把上下文渲染到视图
    // Stroke描边
    CGContextStrokePath(ctx);
}

- (void)draw2Line
{
    // 1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // 2.拼接路径
    UIBezierPath *path = [UIBezierPath bezierPath];

    // 设置起点
    [path moveToPoint:CGPointMake(10, 125)];

    // 添加一条线到某个点
    [path addLineToPoint:CGPointMake(230, 125)];

    //    // 设置起点
    //    [path moveToPoint:CGPointMake(10, 10)];
    //
    //    // 添加一条线到某个点
    //    [path addLineToPoint:CGPointMake(125, 100)];

    UIBezierPath *path1 = [UIBezierPath bezierPath];

    [path1 moveToPoint:CGPointMake(10, 10)];

    [path1 addLineToPoint:CGPointMake(125, 100)];

    // 3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath);
    CGContextAddPath(ctx, path1.CGPath);

    // 设置绘图状态
    // 设置线宽
    CGContextSetLineWidth(ctx, 10);
    CGContextSetLineCap(ctx, kCGLineCapRound);
    //    CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
    [[UIColor redColor] set];

    // 4.渲染上下文到视图
    CGContextStrokePath(ctx);

}

第二种:下载进度条:

- (void)setProgress:(CGFloat)progress
{
    _progress = progress;
    self.myLabel.text = [NSString stringWithFormat:@"%.2f%%",progress*100];
    //    [self drawRect:self.bounds];
    // 重新绘制
    // 在view上做一个重绘的标记,当下次屏幕刷新的时候,就会调用drawRect.
    [self setNeedsDisplay];
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.

 // 当视图显示的时候会调用 默认只会调用一次
- (void)drawRect:(CGRect)rect
{
     // 1.获取上下文
     CGContextRef ctx = UIGraphicsGetCurrentContext();

     // 2.拼接路径
     CGPoint center = CGPointMake(50, 50);
     CGFloat radius = 50 - 2;
     CGFloat startA = -M_PI_2;
     CGFloat endA = -M_PI_2 + _progress * M_PI * 2;
     UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

     // 3.把路径添加到上下文
     CGContextAddPath(ctx, path.CGPath);

     // 4.把上下文渲染到视图
     CGContextStrokePath(ctx);

}

第三种:饼图

- (void)drawRect:(CGRect)rect
{
    // Drawing code

    NSArray *data = @[@25,@25,@50];

    // 1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // 2.拼接路径
    CGPoint center = CGPointMake(125, 125);
    CGFloat radius = 120;
    CGFloat startA = 0;
    CGFloat angle = 0;
    CGFloat endA = 0;

    for (NSNumber *number in data) {
        // 2.拼接路径
        startA = endA;
        angle = number.intValue / 100.0 * M_PI * 2;
        endA = startA + angle;
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
        [path addLineToPoint:center];

        [[UIColor randomColor] set];
        // 把路径添加上下文
        CGContextAddPath(ctx, path.CGPath);

        // 渲染
        CGContextFillPath(ctx);

    }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGFloat a = arc4random_uniform(6);
    //CGFloat a =  arc4random()%6;
    NSLog(@"随机数--%f",a);

    [self setNeedsDisplay];
}

- (void)drawPie
{
    // 1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // 2.拼接路径
    CGPoint center = CGPointMake(125, 125);
    CGFloat radius = 120;
    CGFloat startA = 0;
    CGFloat angle = 0;
    CGFloat endA = 0;

    // 第一个扇形
    angle = 25 / 100.0 * M_PI * 2;
    endA = startA + angle;
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
    [path addLineToPoint:center];
    // 添加到上下文
    CGContextAddPath(ctx, path.CGPath);
    [[UIColor redColor] set];

    // 渲染
    CGContextFillPath(ctx);

    // 第二个扇形
    startA = endA;
    angle = 25 / 100.0 * M_PI * 2;
    endA = startA + angle;
    UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
    [path1 addLineToPoint:center];
    // 添加到上下文
    CGContextAddPath(ctx, path1.CGPath);
    [[UIColor greenColor] set];
    // 渲染
    CGContextFillPath(ctx);

    // 第三个扇形
    startA = endA;
    angle = 50 / 100.0 * M_PI * 2;
    endA = startA + angle;
    UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
    [path2 addLineToPoint:center];
    // 添加到上下文
    CGContextAddPath(ctx, path2.CGPath);
    [[UIColor blueColor] set];
    // 渲染
    CGContextFillPath(ctx);

}



第四种:柱形图

- (void)drawRect:(CGRect)rect
{
     NSArray *data = @[@25,@25,@50];
     NSInteger count = data.count;

     CGFloat w = rect.size.width / (2 * count - 1);
     CGFloat h = 0;
     CGFloat x = 0;
     CGFloat y = 0;
     CGFloat viewH = rect.size.height;
     // 1.获取上下文
     CGContextRef ctx = UIGraphicsGetCurrentContext();

     for (int i = 0; i < count; i++) {
     h = viewH * [data[i] intValue] / 100.0;
     x = 2 * w * i;
     y = viewH - h;
     // 2.拼接路径
     UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, w, h)];

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

     [[UIColor randomColor] set];

     // 4.渲染
     CGContextFillPath(ctx);
     }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
     [self setNeedsDisplay];
}


第五种:模仿UIImageView

- (void)setImage:(UIImage *)image
 {
     _image = image;
     [self setNeedsDisplay];
}

 // Only override drawRect: if you perform custom drawing.
 // An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
 // Drawing code

     [_image drawInRect:rect];
}



第六种:图形上下文线

- (void)drawRect:(CGRect)rect
{
    // Drawing code

    // 1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // 把ctx拷贝一份放在栈中
    CGContextSaveGState(ctx);

    // 2.拼接路径(绘图的信息)
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(10, 125)];
    [path addLineToPoint:CGPointMake(240, 125)];

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

    // 设置绘图的状态
    [[UIColor redColor] set];
    CGContextSetLineWidth(ctx, 10);
    CGContextSetLineCap(ctx, kCGLineCapRound);

    // 4.渲染
    CGContextStrokePath(ctx);

    // 第二根线
    UIBezierPath *path1 = [UIBezierPath bezierPath];
    [path1 moveToPoint:CGPointMake(125, 10)];
    [path1 addLineToPoint:CGPointMake(125, 240)];
    CGContextAddPath(ctx, path1.CGPath);

    // 把栈顶上下文取出来,替换当前上下文
    CGContextRestoreGState(ctx);

    // 设置绘图的状态
    //    [[UIColor blackColor] set];
    //    CGContextSetLineWidth(ctx, 1);
    //    CGContextSetLineCap(ctx, kCGLineCapButt);

    // 4.渲染
    CGContextStrokePath(ctx);

}



第七种:矩形操作

- (void)drawRect:(CGRect)rect
{
    // Drawing code

    // 1.获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // 注意:你的路径一定放在上下文矩阵操作之后
    // 平移上下文
    CGContextTranslateCTM(ctx, 50, 100);

    // 旋转上下文
    CGContextRotateCTM(ctx, M_PI_4);

    // 缩放上下文
    CGContextScaleCTM(ctx, 0.5, 1.2);

    // 2.拼接路径
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-50, -100, 150, 200)];

    // 3.把路径添加到上下文
    CGContextAddPath(ctx, path.CGPath);

    [[UIColor redColor] set];

    // 4.渲染
    CGContextFillPath(ctx);

}

iOS开发者交流群:446310206

iOS开发者交流QQ群: 446310206  欢迎加入(demo在这里)!

时间: 2024-10-29 09:44:53

iOS中 CoreGraphics快速绘图(详解) 韩俊强的博客的相关文章

iOS中 HTTP/Socket/TCP/IP通信协议详解 韩俊强的博客

版权声明:本文为博主原创文章,未经博主允许不得转载. 每日更新关注:http://weibo.com/hanjunqiang  新浪微博 简单介绍: [objc] view plain copy // OSI(开放式系统互联), 由ISO(国际化标准组织)制定   // 1. 应用层   // 2. 表示层   // 3. 会话层   // 4. 传输层   // 5. 网络层   // 6. 数据链接层   // 7. 物理层      // TCP/IP, 由美国国防部制定   // 1. 

iOS中 扫描二维码/生成二维码详解 韩俊强的博客

最近大家总是问我有没有关于二维码的demo,为了满足大家的需求,特此研究了一番,希望能帮到大家! 每日更新关注:http://weibo.com/hanjunqiang  新浪微博 指示根视图: self.window.rootViewController = [[UINavigationController alloc]initWithRootViewController:[SecondViewController new]]; 每日更新关注:http://weibo.com/hanjunqi

iOS中 Realm的学习与使用 韩俊强的博客

iOS开发者交流QQ群:446310206  有问题或技术交流可以咨询!欢迎加入! 这篇直接搬了一份官方文档过来看的 由于之前没用markdown搞的乱七八糟的 所以重新做了一份 后面看到官网的中文文档更新不及时看着英文翻译了一点 搞的更乱了 :( 英文好的直接点右边->官方OC文档 Realm是一个移动端的数据库,Realm是SQLite和CoreData的替代者.它可以节省你成千上万行代码和数周的工作,并且让你精巧的制作出令人惊叹的用户体验. 文档版本 0.93.2在github上获取 需求

iOS中 最新微信支付/最全的微信支付教程详解 韩俊强的博客

亲们, 首先让我们来看一下微信支付的流程吧. 1. 注册微信开放平台,创建应用获取appid,appSecret,申请支付功能,申请成功之后会返回一些参数. 2. 下载微信支付sdk 3. 客户端请求订单,后台与微信后台交互,返回给客户端支付参数 4. 调用微信客户端,由微信客户端和微信服务器打交道: 5. 客户端和服务端都会收到支付结果:(前台消息不可靠,我们需要去后台验证,如果后台没有收到支付通知,后台去微信服务器验证然后将结果返回给客户端) 支付流程图(大家看一下支付流程). 那么我们现在

iOS中 语音识别功能/语音转文字教程详解 韩俊强的博客

原文地址:http://blog.csdn.net/qq_31810357/article/details/51111702 前言:最近研究了一下语音识别,从百度语音识别到讯飞语音识别:首先说一下个人针对两者的看法,讯飞毫无疑问比较专业,识别率也很高真对语音识别是比较精准的,但是很多开发者和我一样期望离线识别,而讯飞离线是收费的:请求次数来讲,两者都可以申请高配额,针对用户较多的几乎都一样.基于免费并且支持离线我选择了百度离线语音识别.比较简单,UI设计多一点,下面写一下教程: 1.首先:需要的

iOS中 蓝牙2.0详解/ios蓝牙设备详解 韩俊强的博客

每日更新关注:http://weibo.com/hanjunqiang  新浪微博 整体布局如下:     程序结构如右图: 每日更新关注:http://weibo.com/hanjunqiang  新浪微博 ========================================================================== 指定根视图: RootViewController * rootVC = [[RootViewController alloc] init]

iOS中 断点下载详解 韩俊强的博客

布局如下: 基本拖拉属性: #import "ViewController.h" #import "AFNetworking.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UILabel *progressLabel; @property (weak, nonatomic) IBOutlet UIProgressView *progressView; @property (n

iOS中 Apple开发相关邮箱汇总 韩俊强的博客

每周更新关注:http://weibo.com/hanjunqiang  新浪微博!手机加iOS开发者交流QQ群: 446310206 收集整理下来的邮箱列表,附上简单说明,希望对广大开发者有帮助:------------------------------------------------------------------------------------------------------- AppReview@apple.com-应用在提交后(处于"审核中"),应用和应用内

iOS中 UITableViewCell cell划线那些事 韩俊强的博客

每日更新关注:http://weibo.com/hanjunqiang  在开发中经常遇到cell分割线显示不全或者想自定义线的宽高等; 最近总结了一下,希望帮到大家: 1.不想划线怎么办? TableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; // 设置系统默认线的样式 -(void)viewDidLayoutSubviews { if ([TableView respondsToSelector:@select