首页 关于 微信公众号
欢迎关注我的微信公众号

Quartz2D

1.说明

Quartz 2D 是一个二维绘图引擎,同时支持ios和mac系统。Quartz 2D能完成以下工作:

图形上下文

图形上下文(Graphics Context):是一个CGContextRef类型的数据。

作用:

2.画线

1)画一条线

画如下图所示的一条线,有以下几种方式:

第一种方式:

- (void)drawLine1
{
// 1. 获取图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
    
// 2. 描述路径
CGMutablePathRef path= CGPathCreateMutable();
    
// 设置起点
CGPathMoveToPoint(path, NULL, 50, 50);
    
// 添加一根线到某个点
CGPathAddLineToPoint(path, NULL, 200, 200);
    
// 3. 把路径添加到上下文
CGContextAddPath(ctx, path);
    
// 4. 渲染上下文
CGContextStrokePath(ctx); }

第二种方式:

- (void)drawLine2
{
    // 1. 获取图形上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    // 描述路径
    // 设置起点
    CGContextMoveToPoint(ctx, 50, 50);
    CGContextAddLineToPoint(ctx,200, 200);
    // 渲染上下文
    CGContextStrokePath(ctx);

}

第三种方式:

- (void)drawLine3
{
    // 贝瑟尔路径
    // 创建路径
    UIBezierPath *path = [UIBezierPath bezierPath];
    // 设置起点
    [path moveToPoint:CGPointMake(50, 50)];
    // 添加一根线到某个点
    [path addLineToPoint:CGPointMake(200, 200)];
    // 绘制路径
    [path stroke];
}

2)画两条线

1.如图所示

代码:

- (void)drawCtxState
{
    // 获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 描述路径
    CGContextMoveToPoint(ctx, 50, 50);
    CGContextAddLineToPoint(ctx, 100, 50);
    // 默认下一根线的起点就是上一根线的终点
    CGContextAddLineToPoint(ctx, 100, 200);
    
    // 设置绘图状态
    // 颜色
    [[UIColor redColor] setStroke];
    
    // 线宽
    CGContextSetLineWidth(ctx, 20);
    
    // 设置连接样式
    CGContextSetLineJoin(ctx, kCGLineJoinBevel);
    
    // 设置顶角样式
    CGContextSetLineCap(ctx, kCGLineJoinRound);
    
    // 渲染上下文
    CGContextStrokePath(ctx);
}
  1. 如图所示:

代码:

- (void)drawUIBezierState
{
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(50, 50)];
    [path addLineToPoint:CGPointMake(200, 200)];
    path.lineWidth = 10;
    [[UIColor redColor] set];
    [path stroke];
    
    UIBezierPath *path1 = [UIBezierPath bezierPath];
    [path1 moveToPoint:CGPointMake(0, 0)];
    [path1 addLineToPoint:CGPointMake(30, 60)];
    [[UIColor greenColor] set];
    path1.lineWidth = 3;
    [path1 stroke];

}

3)绘制曲线

如图所示:

代码:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    // 绘制曲线
    // 原生绘制方法
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    // 描述路径
    // 设置起点
    CGContextMoveToPoint(ctx, 50, 50);
    
    // cpx:控制点的x  cpy:控制点的y
    CGContextAddQuadCurveToPoint(ctx, 150, 20, 250, 50);
    
    // 渲染上下文
    CGContextStrokePath(ctx);
}

3.画图形

1)圆角矩形

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 20, 200, 200) cornerRadius:10];
    [path stroke];

2)圆形

// 线条圆形
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 20, 200, 200) cornerRadius:100];
[path stroke];
// 填充
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 20, 200, 200) cornerRadius:100];
[path fill];  // 填充:必须是一个完整的封闭路径

3)圆弧

    // center: 圆心
    // startAngle : 弧度
    // clockwise: YES:顺时针  NO:逆时针
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(125, 125) radius:100 startAngle:0 endAngle:M_PI_2 clockwise:YES];
    [path stroke];

4)扇形

 CGPoint center = CGPointMake(125, 125);
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_2 clockwise:YES];
    // 添加一根线到圆形
    [path addLineToPoint:center];
    
    // 关闭路径:默认从路径的终点到起点
    [path closePath];
    
    // 填充 : 必须是一个封闭的路径,默认会自动关闭路径
    [path fill];

4.重绘

说明

只要在 drawRect:(CGRect)rect 方法中才能获取到图形上下文。但是图形上下文我们自己创建不了,只能由系统创建,然后传递给我们。所以 drawRect 不能手动调用。

重绘图形使用 [self setNeedsDisplay] ,系统会首先创建于view相关联的上下文,然后调用 drawRect 。

例子

我们要写一个下载进度条的demo,如图所示:

关键代码:

ViewController:

#import "ViewController.h"
#import "DownloadProgressView.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *progressValueLab;

@property (weak, nonatomic) IBOutlet DownloadProgressView *progressView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)slideProgress:(UISlider *)slider {
    self.progressValueLab.text = [NSString stringWithFormat:@"%.2f%%",slider.value * 100];
    self.progressView.progress = slider.value;
}

DownloadProgressView:

#import "DownloadProgressView.h"

@implementation DownloadProgressView


- (void)setProgress:(float)progress
{
    _progress = progress;
    
    // 重绘
    [self setNeedsDisplay];
    
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    // 创建贝瑟尔路径
    CGFloat radius = rect.size.width * 0.5;
    CGPoint center = CGPointMake(radius, radius);
    
    CGFloat endA = -M_PI_2 + _progress * M_PI * 2;
    
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius-10 startAngle:-M_PI_2 endAngle:endA clockwise:YES];
    [path stroke];
}	

示例代码: DownloadProgress

5.饼状图

效果图如图所示:

代码:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    NSArray *array = @[@25,@25,@50];
    
    CGFloat radius = rect.size.width * 0.5;
    CGPoint center = CGPointMake(radius, radius);
    
    CGFloat startA = 0;
    CGFloat angle = 0;
    CGFloat endA = 0;
    
    for (int i = 0; i < array.count; i++) {
        startA = endA;
        angle = [array[i] doubleValue] / 100.0 * M_PI * 2;
        endA = startA + angle;
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
        [path addLineToPoint:center];
        
        [[self colorRandom] set];
        
        [path fill];
    }
}
- (UIColor *)colorRandom
{
    CGFloat r = arc4random_uniform(256) / 255.0;
    CGFloat g = arc4random_uniform(256) / 255.0;
    CGFloat b = arc4random_uniform(256) / 255.0;
    return [UIColor colorWithRed:r green:g blue:b alpha:1];
}

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

6.柱状图

效果图如图所示:

代码:

- (void)drawRect:(CGRect)rect {
    
    NSArray *arr = @[@25,@25,@50];
    
    CGFloat x = 0;
    CGFloat y = 0;
    CGFloat w = 0;
    CGFloat h = 0;
    
    for (int i = 0; i < arr.count; i++) {
        w = rect.size.width / (2 * arr.count -1);
        x = 2 * w * i;
        h = [arr[i] floatValue] / 100.0 * rect.size.height;
        y = rect.size.height - h;
        
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, w, h)];
        
        [[self colorRandom] set];
        
        [path fill];
    }
}

7.绘制文字和图片

1)绘制文字

效果图如图:

代码:

// 绘制文字
- (void)drawWords
{
    NSString *words = @"Hello World!!";
    
    // Text attributes
    NSMutableDictionary *textDict = [NSMutableDictionary dictionary];
    
    // 文字颜色
    textDict[NSForegroundColorAttributeName] = [UIColor redColor];
    
    // 字体
    textDict[NSFontAttributeName] = [UIFont systemFontOfSize:30];
    
    // 设置文字的空心颜色和宽度
    textDict[NSStrokeWidthAttributeName] = @3;
    textDict[NSStrokeColorAttributeName] = [UIColor yellowColor];
    
    // 创建阴影对象
    NSShadow *shadow = [[NSShadow alloc] init];
    shadow.shadowColor = [UIColor greenColor];
    shadow.shadowOffset = CGSizeMake(4, 4);
    shadow.shadowBlurRadius = 3;
    textDict[NSShadowAttributeName] = shadow;
    
    // 富文本:给普通的文字添加颜色,字体大小
//    [words drawAtPoint:CGPointZero withAttributes:textDict];  // 不会自动换行
    [words drawInRect:self.bounds withAttributes:textDict]; // 会自动换行

}

2)绘制图片

绘制图片代码:

// 绘制图片
- (void)drawImages
{
    // 超出裁剪区域的内容全部裁减掉,裁剪必须放在绘制之前
    UIRectClip(CGRectMake(0, 0, 50, 50));
    UIImage *image = [UIImage imageNamed:@"001"];
    
    // 绘图
    [image drawAsPatternInRect:self.bounds];
}

3)自定义UIImageView

代码:

TVUImageView.h

#import <UIKit/UIKit.h>

@interface TVUImageView : UIView
@property (nonatomic,strong) UIImage *image;

- (instancetype)initWithImage:(UIImage *)image;
@end

TVUImageView.m

#import "TVUImageView.h"

@implementation TVUImageView


- (instancetype)initWithImage:(UIImage *)image
{
    self = [super initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
    if (self) {
        _image = image;
    }
    return self;
}

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


@end

4)绘图时使用定时器

在绘图的时候很少使用 NSTimer 开启一个定时器,因为其调度优先级别比较低,并不会准时调用。 因此需要使用 CADisplayLink 开启一个定时器。

下面来看一个雪花的例子:

#import "DrawView.h"
static CGFloat _snowY = 0;
@implementation DrawView


// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
    
    UIImage *image = [UIImage imageNamed:@"雪花"];
    [image drawAtPoint:CGPointMake(50, _snowY)];
    _snowY += 10;
    if (_snowY > rect.size.height) {
        _snowY = 0;
    }
    
}

/* 
 如果在绘图的时候需要用到定时器,通常
 NSTimer很少用于绘图,因为调度优先级比较低,并不会准时调用  */
- (void)awakeFromNib
{
    CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeChange)];
    [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

- (void)timeChange
{
    // 注意:这个方法并不会马上调用drawRect,这个方法只是给当前控件添加刷新的标记,等下一次屏幕刷新的时候才会调用drawRect
    [self setNeedsDisplay];
}

@end

自定义View

首先,得有图形上下文,因为它能保存绘图信息,并且决定着绘制到什么地方去。其次,那个图形上下文必须跟view相关联,才能将内容绘制到view上面。

自定义view的步骤:

Blog

Opinion

Project