Quartz2D

Quartz2D以及drawRect的重繪機(jī)制
字?jǐn)?shù)1487 閱讀21 評(píng)論1 喜歡1
一、什么是Quartz2D

Quartz2D是?個(gè)二維繪圖引擎,同時(shí)支持iOS和Mac系統(tǒng)
Quartz2D的API是純C語(yǔ)?言的Quartz2D的API來(lái)自于Core Graphics框架
Quartz2D的數(shù)據(jù)類(lèi)型和函數(shù)基本都以CG作為前綴,例如下面2個(gè)類(lèi)型:
1.CGContextRef
2.CGPathRef
二、Quartz 2D能完成的工作
繪制圖形 : 線條\三角形\矩形\圓\弧,折線圖,餅圖,柱狀圖
繪制文字
繪制\生成圖片(圖像)
讀取\生成PDF
屏幕截圖\裁剪圖片(例如截取游戲的五殺. 例如將矩形裁剪成圓形)
自定義UI控件
畫(huà)板(可以在畫(huà)板上畫(huà)畫(huà))
手勢(shì)解鎖
圖片加水印

三、Quartz2D在iOS開(kāi)發(fā)中的價(jià)值

為了便于搭建美觀的UI界面,iOS提供了UIKit框架,里面有各種各樣的UI控件,
利用UIKit框架提供的控件,拼拼湊湊,能搭建和現(xiàn)實(shí)一些簡(jiǎn)單、常見(jiàn)的UI界面。
UILabel:顯示文字
UIImageView:顯示圖片
UIButton:同時(shí)顯示圖片和文字(能點(diǎn)擊)
......
但是,有些UI界面極其復(fù)雜、而且比較個(gè)性化,用普通的UI控件無(wú)法實(shí)現(xiàn),這時(shí)可以利用Quartz2D技術(shù)將控件內(nèi)部的結(jié)構(gòu)畫(huà)出來(lái),類(lèi)似自定義控件.其實(shí),iOS中大部分控件的內(nèi)容都是通過(guò)Quartz2D畫(huà)出來(lái)的,因此,Quartz2D在iOS開(kāi)發(fā)中很重要的一個(gè)價(jià)值是:自定義view(自定義UI控件)
四、圖形上下文

圖形上下文(Graphics Context)是一個(gè)CGContextRef類(lèi)型的數(shù)據(jù).
圖形上下文的作用
保存繪圖信息、繪圖狀態(tài)
相當(dāng)于畫(huà)布,不同類(lèi)型的畫(huà)布就是決定著畫(huà)得內(nèi)容將展示在哪里。
相同的一套繪圖序列,指定不同的Graphics Context,就可將相同的圖像繪制到不同的目標(biāo)上,Quartz2D提供了以下幾種類(lèi)型的Graphics Context:
Bitmap Graphics Context 位圖上下文,在這個(gè)上下文上繪制或者渲染的內(nèi)容,可以獲取成圖片(需要主動(dòng)創(chuàng)建一個(gè)位圖上下文來(lái)使用,使用完畢,一定要銷(xiāo)毀)
PDF Graphics Context
Window Graphics Context
Layer Graphics Context 圖層上下文,針對(duì)UI控件的上下文
Printer Graphics Context

輸出方式.png
五、利用Quartz2D繪制內(nèi)容到自定義的view上

1.新建一個(gè)類(lèi),繼承自UIView
2.實(shí)現(xiàn)- (void)drawRect:(CGRect)rect方法,然后在這個(gè)方法中,取得跟當(dāng)前view相關(guān)聯(lián)的圖形上下文。
3.繪制相應(yīng)的圖形內(nèi)容
4.利用圖形上下文將繪制的所有內(nèi)容渲染顯示到view上面
六、核心方法drawRect:

為什么要實(shí)現(xiàn)drawRect:方法才能繪圖到view上?
因?yàn)樵赿rawRect:方法中才能取得跟view相關(guān)聯(lián)的圖形上下文
drawRect:方法在什么時(shí)候被調(diào)用?
當(dāng)view第一次顯示到屏幕上時(shí)(被加到UIWindow上顯示出來(lái))
調(diào)用view的setNeedsDisplay或者setNeedsDisplayInRect:時(shí).
注意4點(diǎn):
手動(dòng)調(diào)用drawRect:方法,不會(huì)自動(dòng)創(chuàng)建跟View相關(guān)聯(lián)的上下文。應(yīng)該
調(diào)用setNeedsDisplay方法,系統(tǒng)底層會(huì)自動(dòng)調(diào)用drawRect,告訴系統(tǒng)重新繪制View.這樣,系統(tǒng)底層會(huì)自動(dòng)創(chuàng)建跟View相關(guān)聯(lián)的上下文
setNeedsDisplay底層會(huì)調(diào)用drawRect,并不是立馬調(diào)用的.只是設(shè)了一個(gè)調(diào)用的標(biāo)志.調(diào)用時(shí)刻是等下一次屏幕刷新時(shí)才去調(diào)用drawRect。屏幕每一秒刷新30-60秒次,所以1秒調(diào)用drawRect方法大概30-60次,速度非常快哦
view內(nèi)部有個(gè)layer(圖層)屬性,drawRect:方法中取得的是一個(gè)Layer Graphics Context,因此,繪制的東西其實(shí)是繪制到view的layer上去了
View之所以能顯示東西,完全是因?yàn)樗鼉?nèi)部的layer
七、Quartz2D繪圖的代碼步驟

1.獲得圖形上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();
2.拼接路徑(下面代碼是繪制一條線段)

CGContextMoveToPoint(ctx, 10, 10);
CGContextAddLineToPoint(ctx, 100, 100);
3.繪制路徑

CGContextStrokePath(ctx); // CGContextFillPath(ctx);
八、常用拼接路徑函數(shù)

新建一個(gè)起點(diǎn)

void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)
添加新的線段到某個(gè)點(diǎn)

void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
添加一個(gè)矩形

void CGContextAddRect(CGContextRef c, CGRect rect)
添加一個(gè)橢圓

void CGContextAddEllipseInRect(CGContextRef context, CGRect rect)
添加一個(gè)圓弧

void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y,
CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
九、常用繪制路徑函數(shù)

一般以CGContextDraw、CGContextStroke、CGContextFill開(kāi)頭的函數(shù),都是用來(lái)繪制路徑的
Mode參數(shù)決定繪制的模式

void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
繪制空心路徑

void CGContextStrokePath(CGContextRef c)
繪制實(shí)心路徑

void CGContextFillPath(CGContextRef c)
十、矩陣操作

利用矩陣操作,能讓繪制到上下文中的所有路徑一起發(fā)生變化
縮放

void CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy)
旋轉(zhuǎn)

void CGContextRotateCTM(CGContextRef c, CGFloat angle)
平移

void CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty)
十一、案例

特別注意:
M_PI的含義:π
M_PI * 2的含義:2π

M_PI_2的含義:π/2
M_PI / 2的含義:π/2

// 畫(huà)的圖形路徑
//bezierPathWithArcCenter:弧所在的圓心
//radius:圓的半徑
//startAngle:開(kāi)始角度,圓的最右側(cè)為0度
//endAngle:截至角度,向下為正,向上為負(fù).
//clockwise:時(shí)針的方向,yes:順時(shí)針 no:逆時(shí)針
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:self.center radius:radius startAngle:startA endAngle:endA clockwise:NO];

畫(huà)矩形、正方形

  • (void)drawRect:(CGRect)rect {
    //1.獲取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.描述路徑
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 200, 200)];
    //3.把路徑添加到上下文
    CGContextAddPath(ctx, path.CGPath);

    [[UIColor redColor] set];// 路徑的顏色

    //4.把上下文的內(nèi)容渲染到View的layer.
    // CGContextStrokePath(ctx);// 描邊路徑
    CGContextFillPath(ctx);// 填充路徑

}

描邊矩形.png

填充矩形.png
畫(huà)扇形

  • (void)drawRect:(CGRect)rect {

    CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
    CGFloat radius = rect.size.width * 0.5 - 10;
    CGFloat startA = 0;
    CGFloat endA = -M_PI_2;
    // 畫(huà)弧的路徑
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:NO];
    // 添加一根線到圓心
    [path addLineToPoint:center];
    // 閉合路徑
    [path closePath];
    // 路徑顏色
    [[UIColor redColor] set];
    // 填充路徑
    [path fill];
    // 描邊路徑
    // [path stroke];

}

描邊扇形.png

填充扇形.png
畫(huà)圓形

  • (void)drawRect:(CGRect)rect {
    //1.獲取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.描述路徑
    // cornerRadius:圓角半徑。矩形的寬高都為200,如果圓角為100,那么兩個(gè)角之間弧線上任意一點(diǎn)到矩形中心的距離都為100,所以為圓形
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 200, 200) cornerRadius:100];
    //3.把路徑添加到上下文
    CGContextAddPath(ctx, path.CGPath);

    [[UIColor redColor] set];// 路徑的顏色

    //4.把上下文的內(nèi)容渲染到View的layer.
    // CGContextStrokePath(ctx);// 描邊路徑
    CGContextFillPath(ctx);// 填充路徑

}

描邊圓形.png

填充圓形.png
畫(huà)圓角矩形

  • (void)drawRect:(CGRect)rect {
    //1.獲取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.描述路徑
    // cornerRadius:圓角半徑。
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 200, 200) cornerRadius:50];
    //3.把路徑添加到上下文
    CGContextAddPath(ctx, path.CGPath);

    [[UIColor redColor] set];// 路徑的顏色

    //4.把上下文的內(nèi)容渲染到View的layer.
    CGContextStrokePath(ctx);// 描邊路徑
    // CGContextFillPath(ctx);// 填充路徑

}

描邊圓角矩形.png

填充圓角矩形.png
畫(huà)直線

  • (void)drawRect:(CGRect)rect {

    //1.獲取跟View相關(guān)聯(lián)的上下文(uigraphics開(kāi)頭)
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    //2.描述路徑
    //一條路徑可以繪制多條線 路徑:path 路徑繪制多條線:path使用了兩次(2次的起點(diǎn)到終點(diǎn)),都是將線添加到某個(gè)點(diǎn)
    UIBezierPath *path = [UIBezierPath bezierPath];
    //設(shè)置起點(diǎn)
    [path moveToPoint:CGPointMake(50, 150)];
    //添加一根線Line到某個(gè)點(diǎn)
    [path addLineToPoint:CGPointMake(250, 50)];

    //畫(huà)第二根線
    [path moveToPoint:CGPointMake(50, 250)];
    [path addLineToPoint:CGPointMake(250, 100)];

//設(shè)置線寬
CGContextSetLineWidth(ctx, 20);
//設(shè)置線的連接樣式
CGContextSetLineJoin(ctx, kCGLineJoinBevel);
//設(shè)置線的頂角樣式
CGContextSetLineCap(ctx, kCGLineCapRound);// 圓角線條
//設(shè)置線條顏色
[[UIColor redColor] set];

//3.把路徑添加到上下文
CGContextAddPath(ctx, path.CGPath);
//4.把上下文當(dāng)中繪制的內(nèi)容渲染到跟View關(guān)聯(lián)的layer
CGContextStrokePath(ctx);

}

線條.png

相交線條.png
畫(huà)不規(guī)則圖形+添加點(diǎn)擊事件

CustomView.h文件

import <UIKit/UIKit.h>

@interface CustomView : UIView
@property (nonatomic ,strong) UIBezierPath *bezierPath;
@property (nonatomic, strong) UIColor *fillColor;

@end
CustomView.m文件

import "CustomView.h"

@implementation CustomView

-(void)drawRect:(CGRect)rect{
_bezierPath = [UIBezierPath bezierPath];
[_bezierPath moveToPoint:CGPointMake(320, 70)];
[_bezierPath addLineToPoint: CGPointMake(30, 130)];
[_bezierPath addLineToPoint: CGPointMake(80, 400)];
[_bezierPath addLineToPoint: CGPointMake(370, 570)];
[_bezierPath closePath];

//設(shè)置填充色(fillColor屬性存儲(chǔ)著外界賦給它的黃色)
[_fillColor setFill];
// 填充路徑
[_bezierPath fill];

//設(shè)置描邊色
[UIColor.redColor setStroke];
 _bezierPath.lineWidth = 4;
// 描邊路徑
[_bezierPath stroke];

}
@end
ViewController.m文件

import "ViewController.h"

import "CustomView.h"

@interface ViewController ()
@property (nonatomic, strong) CustomView *customView;
@end

@implementation ViewController

  • (void)viewDidLoad {

    //創(chuàng)建充滿整個(gè)屏幕的自定義View 并在自定義View中繪制圖形
    _customView = [[CustomView alloc] initWithFrame:self.view.bounds];
    _customView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:_customView];

    //給自定義View添加點(diǎn)擊事件
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(TapGestureRecognizer:)];
    [_customView addGestureRecognizer:tap];

}

  • (void)TapGestureRecognizer:(UITapGestureRecognizer *)gesture{
    // 獲得手指在當(dāng)前View上的點(diǎn)(位置)
    CGPoint tapPoint = [gesture locationInView:_customView];

    //判斷點(diǎn)是否在繪制的路徑內(nèi)部
    if ([_customView.bezierPath containsPoint:tapPoint]){
    _customView.fillColor = [UIColor yellowColor];
    // 重繪
    [_customView setNeedsDisplay];
    }
    }

@end

不規(guī)則圖形+點(diǎn)擊事件.gif
畫(huà)曲線

本塞爾曲線原理

貝塞爾曲線.gif

  • (void)drawRect:(CGRect)rect {
    //1.獲取跟View相關(guān)聯(lián)的上下文.
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.描述路徑
    UIBezierPath *path = [UIBezierPath bezierPath];
    //畫(huà)曲線,設(shè)置起點(diǎn).還有一個(gè)控制點(diǎn)(用來(lái)控制曲線的方向跟彎曲程度)
    //設(shè)置起點(diǎn).
    [path moveToPoint:CGPointMake(10, 150)];
    //添加一要曲線到某個(gè)點(diǎn)
    [path addQuadCurveToPoint:CGPointMake(200, 150) controlPoint:CGPointMake(150, 10)];
    //3.把路徑添加到上下文當(dāng)中.
    CGContextAddPath(ctx, path.CGPath);
    //4.把上下文的內(nèi)容渲染View上.
    CGContextStrokePath(ctx);

}

曲線.png
畫(huà)餅圖

做法1:

  • (void)drawRect:(CGRect)rect {
    NSArray *dataArray = @[@25,@25,@50];
    // 畫(huà)弧
    CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
    // 半徑
    CGFloat radius = rect.size.width * 0.5 - 10;

    CGFloat startA = 0;
    CGFloat angle = 0;
    CGFloat endA = 0;

    for (NSNumber *num in dataArray) {
    startA = endA;
    // 遍歷出第一個(gè)對(duì)象25,angle =25/100 *2π,即angle = π/2,所以為1/4圓,
    angle = num.intValue / 100.0 * M_PI * 2;
    // 截至角度= 開(kāi)始的角度+ 遍歷出的對(duì)象所占整個(gè)圓形的角度
    endA = startA + angle;
    // 順勢(shì)針畫(huà)貝塞爾曲線
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
    // 設(shè)置隨機(jī)顏色
    [[self randomColor] set];
    // 添加一根線到圓心
    [path addLineToPoint:center];
    // 填充路徑
    // [path fill];
    // 描邊路徑
    [path stroke];
    }

}

描邊餅圖.png

填充餅圖.png

  • (void)drawRect:(CGRect)rect {
    NSArray *dataArray = @[@25,@25,@50];
    // 畫(huà)弧
    CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
    // 半徑
    CGFloat radius = rect.size.width * 0.5 - 10;
    CGFloat startA = 0;
    CGFloat angle = 0;
    CGFloat endA = 0;

    for (NSNumber *num in dataArray) {

      startA = endA;
      // 遍歷出第一個(gè)對(duì)象25,angle =25/100 *2π,即angle = π/2,所以為1/4圓,
      angle = num.intValue / 100.0 * M_PI * 2;
      // 截至角度= 開(kāi)始的角度+ 遍歷出的對(duì)象所占整個(gè)圓形的角度
      endA = startA + angle;
      // 順勢(shì)針畫(huà)貝塞爾曲線
      UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
      // 設(shè)置隨機(jī)顏色
      [[self randomColor] set];
      // 添加一根線到圓心
      [path addLineToPoint:center];
      // 填充路徑
     [path fill];
      // 描邊路徑
    

// [path stroke];
}
}

//隨機(jī)生成一個(gè)顏色

  • (UIColor *)randomColor {

    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<UITouch *> *)touches withEvent:(UIEvent *)event {

    //重繪
    [self setNeedsDisplay];

}

重繪.gif
做法2:

  • (void)drawRect:(CGRect)rect {

    CGPoint center = CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.height * .5);
    CGFloat radius = self.bounds.size.width * 0.5 - 10;
    CGFloat startA = 0;
    CGFloat endA = 25 / 100.0 * M_PI * 2;
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
    [[UIColor redColor] set];
    //添加一根線到圓心
    [path addLineToPoint:center];
    [path fill];

    //第二個(gè)扇形
    startA = endA;
    CGFloat angle = 25 / 100.0 * M_PI * 2;
    endA = startA + angle;
    UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
    [[UIColor greenColor] set];
    //添加一根線到圓心
    [path2 addLineToPoint:center];
    [path2 fill];

    startA = endA;
    angle = 50 / 100.0 * M_PI * 2;
    endA = startA + angle;
    UIBezierPath *path3 = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
    [[UIColor blueColor] set];
    //添加一根線到圓心
    [path3 addLineToPoint:center];
    [path3 fill];}

//隨機(jī)生成一個(gè)顏色

  • (UIColor *)randomColor {

    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<UITouch *> *)touches withEvent:(UIEvent *)event {

    //重繪
    [self setNeedsDisplay];

}

餅圖-做法2.png
畫(huà)文字

  • (void)drawRect:(CGRect)rect {
    NSString *str = @"簡(jiǎn)書(shū):CoderZb";

    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    //設(shè)置字體
    dict[NSFontAttributeName] = [UIFont systemFontOfSize:30];
    //設(shè)置顏色
    dict[NSForegroundColorAttributeName] = [UIColor redColor];
    //設(shè)置描邊
    dict[NSStrokeColorAttributeName] = [UIColor blueColor];
    dict[NSStrokeWidthAttributeName] = @3;
    //設(shè)置陰影
    NSShadow *shadow = [[NSShadow alloc] init];
    shadow.shadowColor = [UIColor greenColor];
    shadow.shadowOffset = CGSizeMake(-2, -2);
    shadow.shadowBlurRadius = 3;
    dict[NSShadowAttributeName] = shadow;

    //設(shè)置文字的屬性
    //drawAtPoint不會(huì)自動(dòng)換行
    //[str drawAtPoint:CGPointMake(0, 0) withAttributes:dict];
    //drawInRect會(huì)自動(dòng)換行
    [str drawInRect:self.bounds withAttributes:dict];

}

文字.png
模擬系統(tǒng)UIImageView是如何畫(huà)出圖片的
底層調(diào)用了 [self setNeedsDisplay];進(jìn)行重繪,接著調(diào)用drawRect:畫(huà)出圖片

ViewController.m文件

import "ViewController.h"

import "ZBImageView.h"

@interface ViewController ()
@end

@implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];
    ZBImageView *ImageV = [[ZBImageView alloc] init];
    ImageV.frame = CGRectMake(0, 0,self.view.frame.size.width, 400);
    ImageV.image = [UIImage imageNamed:@"AA"];
    [self.view addSubview:ImageV];
    }
    @end
    ZBImageView.h文件

import <UIKit/UIKit.h>

@interface ZBImageView : UIView

@property (nonatomic ,strong) UIImage *image;

@end
ZBImageView.m文件

import "ZBImageView.h"

@implementation ZBImageView

  • (void)setImage:(UIImage *)image {
    _image = image;
    //重繪
    [self setNeedsDisplay];
    }

  • (void)drawRect:(CGRect)rect {

    [self.image drawInRect:rect];
    NSLog(@"%s",func);
    }

@end

import "VCView.h"

@implementation VCView

  • (void)awakeFromNib {

    //添加定時(shí)器
    //[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(update) userInfo:nil repeats:YES];

    //多久調(diào)用update方法?當(dāng)下一次屏幕刷新時(shí)調(diào)用(屏幕每一秒刷新30-60秒次,所以1秒調(diào)用update方法大概30-60次)
    CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];

    //想要讓CADisplayLink工作, 必須得要添加到運(yùn)行循環(huán)當(dāng)中.
    [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

//setNeedsDisplay底層會(huì)調(diào)用drawRect,并不是立馬調(diào)用的.只是設(shè)了一個(gè)調(diào)用的標(biāo)志.調(diào)用時(shí)刻是等下一次屏幕刷新時(shí)才去調(diào)用drawRect

}

static int _snowY = 0;

  • (void)update {

    NSLog(@"%s",func);
    _snowY += 10;
    if (_snowY > self.bounds.size.height) {
    _snowY = 0;
    }
    //重繪
    [self setNeedsDisplay];

}

  • (void)drawRect:(CGRect)rect {

    //加載圖片
    UIImage *image = [UIImage imageNamed:@"雪花"];
    [image drawAtPoint:CGPointMake(0, _snowY)];

}

@end

畫(huà)圖片.png
矩陣之縮放、移動(dòng)、旋轉(zhuǎn)UIView

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

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

    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(200, 100, 200, 100)];
    [[UIColor redColor] set];

    //縮放
    CGContextScaleCTM(ctx, 0.5, 0.5);
    //移動(dòng)
    CGContextTranslateCTM(ctx, 150,200);

    //旋轉(zhuǎn)
    CGContextRotateCTM(ctx, M_PI_4);

CGContextAddPath(ctx, path.CGPath);
CGContextFillPath(ctx);

}

原始.png

縮放.png

移動(dòng).png

旋轉(zhuǎn).png
雪花+CA定時(shí)器+drawRect

  • (void)awakeFromNib {
    //方式1:NSTimer定時(shí)器
    //[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(update) userInfo:nil repeats:YES];

    //方式2:CADisplayLink. 多久調(diào)用update方法?當(dāng)下一次屏幕刷新時(shí)調(diào)用(屏幕每一秒刷新30-60秒次,所以1秒調(diào)用update方法大概30-60次)
    CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];

    //想要讓CADisplayLink工作, 必須得要添加到運(yùn)行循環(huán)當(dāng)中.
    [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

//setNeedsDisplay底層會(huì)調(diào)用drawRect,并不是立馬調(diào)用的.只是設(shè)了一個(gè)調(diào)用的標(biāo)志.調(diào)用時(shí)刻是等下一次屏幕刷新時(shí)才去調(diào)用drawRect

}

static int _snowY = 0;

  • (void)update {

    NSLog(@"%s",func);
    _snowY += 10;
    if (_snowY > self.bounds.size.height) {
    _snowY = 0;
    }
    //重繪
    [self setNeedsDisplay];

}

  • (void)drawRect:(CGRect)rect {

    //加載圖片
    UIImage *image = [UIImage imageNamed:@"雪花"];
    [image drawAtPoint:CGPointMake(0, _snowY)];

}

雪花.gif
畫(huà)水印

import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageV;

@end

@implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

    //生成一張圖片
    //0.加載圖片
    UIImage *oriImage = [UIImage imageNamed:@"壁紙"];
    //1.創(chuàng)建位圖上下文(size:開(kāi)啟多大的上下文,就會(huì)生成多大的圖片)
    UIGraphicsBeginImageContext(oriImage.size);
    //2.把圖片繪制到上下文當(dāng)中
    [oriImage drawAtPoint:CGPointZero];
    //3.繪制水印(雖說(shuō)UILabel可以快速實(shí)現(xiàn)這種效果,但是我們也可以繪制出來(lái))
    NSString *str = @"簡(jiǎn)書(shū):CoderZb";

NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSFontAttributeName] = [UIFont systemFontOfSize:20];
dict[NSForegroundColorAttributeName] = [UIColor redColor];

[str drawAtPoint:CGPointZero withAttributes:dict];
//4.從上下文當(dāng)中生成一張圖片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//5.關(guān)閉位圖上下文
UIGraphicsEndImageContext();


self.imageV.image = newImage;

}
@end

水印.png
裁剪圖片

import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageV;

@end

@implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

    //1.確定邊框的寬度
    CGFloat borderW = 10;
    //2.加載圖片
    UIImage *oriImage = [UIImage imageNamed:@"頭像"];
    //3.開(kāi)啟位圖上下文(大小 原始圖片的寬高度+ 2 *邊框?qū)挾?
    CGSize size = CGSizeMake(oriImage.size.width + 2 * borderW, oriImage.size.height + 2 * borderW);
    UIGraphicsBeginImageContext(size);
    //4.繪制邊框(大圓)
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, size.width, size.height)];
    [[UIColor yellowColor] set];
    [path fill];
    //5.繪制小圓(把小圓設(shè)置成裁剪區(qū)域)
    UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(borderW, borderW, oriImage.size.width, oriImage.size.height)];
    [clipPath addClip];
    //6.把圖片繪制到上下文當(dāng)中
    [oriImage drawAtPoint:CGPointMake(borderW, borderW)];
    //7.從上下文當(dāng)中生成圖片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    //8.關(guān)閉上下文.
    UIGraphicsEndImageContext();
    //9.顯示新圖片
    self.imageV.image = newImage;
    }

裁剪圖片.png
屏幕截圖

import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];
    }

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

//生成圖片
//1.開(kāi)啟一個(gè)位圖上下文
UIGraphicsBeginImageContext(self.view.bounds.size);
//2.把View的內(nèi)容繪制到上下文當(dāng)中
CGContextRef ctx =  UIGraphicsGetCurrentContext();
//UIView內(nèi)容想要繪制到上下文當(dāng)中, 必須使用渲染的方式
[self.view.layer renderInContext:ctx];
//3.從上下文當(dāng)中生成一張圖片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//4.關(guān)閉上下文
UIGraphicsEndImageContext();
//把圖片轉(zhuǎn)成二進(jìn)制流
//NSData *data = UIImageJPEGRepresentation(newImage, 1);
NSData *data = UIImagePNGRepresentation(newImage);

[data writeToFile:@"/Users/zhangbin/Desktop/CoderZbCoderZbCoderZb.jpg" atomically:YES];

}

@end

101.125.gif
配合手勢(shì)裁剪圖片

import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageV;
@property (nonatomic, assign)CGPoint startP;

@property (nonatomic, weak) UIView *coverView;

@end

@implementation ViewController

-(UIView *)coverView {

if (_coverView == nil) {
    //創(chuàng)建UIView
    UIView *coverView = [[UIView alloc] init];
    coverView.backgroundColor = [UIColor blackColor];
    coverView.alpha = 0.7;
    _coverView = coverView;
    [self.view addSubview:coverView];
}
return _coverView;

}

  • (IBAction)pan:(UIPanGestureRecognizer *)pan {

    //獲取當(dāng)前手指所在的點(diǎn)
    CGPoint curP = [pan locationInView:self.imageV];
    // 判斷手勢(shì)的狀態(tài)
    // 要執(zhí)行2,那么1必須執(zhí)行過(guò)。聯(lián)想一下點(diǎn)擊圖片的過(guò)程就理解了。
    // 要執(zhí)行3,那么1,2也必須執(zhí)行過(guò)。
    if(pan.state == UIGestureRecognizerStateBegan) {// 1
    //記錄當(dāng)前手指的開(kāi)始點(diǎn)
    self.startP = curP;

    } else if(pan.state == UIGestureRecognizerStateChanged) {// 2

      //rect
      CGFloat w = curP.x - self.startP.x;
      CGFloat h = curP.y - self.startP.y;
      CGRect rect = CGRectMake(self.startP.x, self.startP.y, w, h);
    
      self.coverView.frame = rect;
    

    }else if(pan.state == UIGestureRecognizerStateEnded) {// 3

    //生成一張圖片
    UIGraphicsBeginImageContext(self.imageV.bounds.size);

    //設(shè)置裁剪區(qū)域
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.coverView.frame];
    [path addClip];

    //2.把UIImageV當(dāng)中的內(nèi)容渲染到上下文當(dāng)中
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [self.imageV.layer renderInContext:ctx];

    //從上下文當(dāng)中獲取圖片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    //關(guān)閉上下文
    UIGraphicsEndImageContext();

    self.imageV.image = newImage;

    [self.coverView removeFromSuperview];
}

}
@end

101.126.gif
配合手勢(shì)擦除圖片

import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageV;

@end

@implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

    self.imageV.userInteractionEnabled = YES;

    //添加手勢(shì)
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [self.imageV addGestureRecognizer:pan];

}

  • (void)pan:(UIPanGestureRecognizer *)pan {

    CGFloat rectWH = 20;
    //獲取當(dāng)前手指的點(diǎn)
    CGPoint curP = [pan locationInView:self.imageV];
    // 當(dāng)手指在(curP.x,curP.y)的位置上時(shí),就讓這個(gè)位置的x和y之分別減去10像素,由此CGRectMake(x, y, rectWH, rectWH)構(gòu)成的形狀就是正方形,以后把這個(gè)正方形作為擦除的區(qū)域
    CGFloat x = curP.x - rectWH * 0.5;
    CGFloat y = curP.y - rectWH * 0.5;
    CGRect rect = CGRectMake(x, y, rectWH, rectWH);

//開(kāi)啟一個(gè)位圖上下文
//UIGraphicsBeginImageContext(self.imageV.bounds.size);
UIGraphicsBeginImageContextWithOptions(self.imageV.bounds.size, NO, 0);



CGContextRef ctx = UIGraphicsGetCurrentContext();
//把UIImageV的內(nèi)容渲染到上下文當(dāng)中
[self.imageV.layer renderInContext:ctx];

//擦除上下文當(dāng)中指定的區(qū)域(即正方形區(qū)域(x, y, rectWH, rectWH) )
CGContextClearRect(ctx, rect);

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
self.imageV.image = newImage;

//關(guān)閉上下文
UIGraphicsEndImageContext();

}

@end

101.127.gif
配合UITouch實(shí)現(xiàn)手勢(shì)解鎖鏈接密碼

部分代碼:

import "ClockView.h"

import "SVProgressHUD/SVProgressHUD.h"

define kDefaultBackNumber @"01258"

@interface ClockView()

@property (nonatomic ,strong) NSMutableArray *selectBtnArray;

@property (nonatomic, assign) CGPoint curP;

@end

@implementation ClockView

  • (NSMutableArray *)selectBtnArray {

    if (_selectBtnArray == nil) {
    _selectBtnArray = [NSMutableArray array];
    }
    return _selectBtnArray;
    }

  • (void)awakeFromNib {
    //添加子控件
    [self setUp];
    }

  • (instancetype)initWithFrame:(CGRect)frame
    {
    self = [super initWithFrame:frame];
    if (self) {
    //添加子控件
    [self setUp];
    }
    return self;
    }

//添加子控件

  • (void)setUp {

    for (int i = 0; i < 9; i++) {

      //創(chuàng)建按鈕
      UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
      btn.userInteractionEnabled = NO;
    
      //如果寫(xiě)成btn.userInteractionEnabled=YES或者不寫(xiě),那么點(diǎn)擊按鈕的響應(yīng)事件由按鈕的高亮狀態(tài)響應(yīng)(處理)了,就不會(huì)傳遞給父控件(上一個(gè)響應(yīng)者)處理了,也就不會(huì)執(zhí)行父控件所在的類(lèi)中的touchesBegan方法了.
    
      //如果寫(xiě)成btn.userInteractionEnabled=NO,即不讓按鈕與用戶交互,響應(yīng)事件傳遞給父控件(ClockView),又因?yàn)樵贑lockView類(lèi)里面寫(xiě)了touchesBegan方法,所以會(huì)執(zhí)行touchesBegan方法
      btn.tag = i;
      //設(shè)置按鈕的圖片
      [btn setImage:[UIImage imageNamed:@"空心圓圈"] forState:UIControlStateNormal];
    
      //設(shè)置選中狀態(tài)下的圖片
      [btn setImage:[UIImage imageNamed:@"gesture_node_selected"] forState:UIControlStateSelected];
    
      [self addSubview:btn];
    

    }
    }

//按功能模塊抽方法
//獲取當(dāng)前手指的點(diǎn)

  • (CGPoint)getCurPoint:(NSSet )touches {//CGPoint后面沒(méi)有,強(qiáng)烈注意
    //1獲取當(dāng)前手指的點(diǎn)

    //1.1當(dāng)用戶用一根手指觸摸屏幕時(shí),會(huì)創(chuàng)建一個(gè)與手指相關(guān)聯(lián)的UITouch對(duì)象
    UITouch *touch = [touches anyObject];
    //1.2手指在當(dāng)前view上的位置
    CGPoint curP = [touch locationInView:self];
    return curP;
    }

//給定一個(gè)點(diǎn),判斷這個(gè)點(diǎn)在不在按鈕身上
//如果沒(méi)有找到符合的條件,直接返回nil.

  • (UIButton *)btnContainsPoint:(CGPoint)point {
    //取出所有的子控件.
    for (UIButton *btn in self.subviews) {
    // 判斷當(dāng)前點(diǎn)在不在按鈕身上.
    if (CGRectContainsPoint(btn.frame, point)) {

          //如果在的話, 讓按鈕成為選中狀態(tài)
    
          return btn;
      }
    

    }
    return nil;
    }

//手指開(kāi)始點(diǎn)擊

  • (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //獲取當(dāng)前手指的點(diǎn)
    CGPoint curP = [self getCurPoint:touches];
    //給定一個(gè)點(diǎn),判斷這個(gè)點(diǎn)在不在按鈕身上
    UIButton *btn = [self btnContainsPoint:curP];
    if(btn && btn.selected == NO) {
    btn.selected = YES;
    //保存選中的按鈕
    [self.selectBtnArray addObject:btn];
    }

}

//手指移動(dòng)

  • (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    //獲取當(dāng)前手指的點(diǎn)
    CGPoint curP = [self getCurPoint:touches];

    //在手指移動(dòng)的過(guò)程中,邊移動(dòng)手指,邊用self.curP保存當(dāng)前手指的點(diǎn)
    self.curP = curP;

    //取出所有的子控件.
    //給定一個(gè)點(diǎn),判斷這個(gè)點(diǎn)在不在按鈕身上
    UIButton *btn = [self btnContainsPoint:curP];
    if(btn && btn.selected == NO) {
    btn.selected = YES;
    //保存選中的按鈕
    [self.selectBtnArray addObject:btn];
    }

    //重繪
    [self setNeedsDisplay];
    }

//手指離開(kāi)

  • (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
    //所有選中按鈕取消選中狀態(tài)

NSMutableString *str = [NSMutableString string];

 for (UIButton *btn in self.selectBtnArray) {
 btn.selected = NO;
 [str appendFormat:@"%ld",btn.tag];

 }

 NSLog(@"%@",str);

// 無(wú)論是畫(huà)路徑還是清空路徑,必須得調(diào)用setNeedsDisplay

//清空路徑
[self.selectBtnArray removeAllObjects];

//清空路徑還必須調(diào)用重繪方法
[self setNeedsDisplay];

 if([str isEqualToString:kDefaultBackNumber]){
     NSLog(@"跳轉(zhuǎn)");
     // 發(fā)出通知
     [[NSNotificationCenter defaultCenter] postNotificationName:@"CoderZb" object:nil userInfo:nil];
 }else{
     [SVProgressHUD showErrorWithStatus:@"手勢(shì)錯(cuò)誤"];
 }

}

  • (void)drawRect:(CGRect)rect {

    if (self.selectBtnArray.count) {//view開(kāi)始顯示之前會(huì)調(diào)用drawRect方法,此時(shí)方法里面沒(méi)有任何值,數(shù)組里面也沒(méi)有值,所以要先判斷,if (self.selectBtnArray.count),如果沒(méi)有值,就不執(zhí)行if里面的內(nèi)容

      //描述路徑
      UIBezierPath *path = [UIBezierPath bezierPath];
      //取出所有選中的按鈕
    
      for (int i = 0; i < self.selectBtnArray.count; i++) {
          //取出每一個(gè)按鈕
          UIButton *btn =  self.selectBtnArray[i];
    
          if (i == 0) {// selectBtnArray數(shù)組中的第0個(gè)對(duì)象
              //讓selectBtnArray數(shù)組中的第0個(gè)對(duì)象按鈕成為路徑的起點(diǎn)。第0個(gè)對(duì)象是9個(gè)按鈕中的任意一個(gè),因?yàn)辄c(diǎn)擊哪個(gè)按鈕由用戶自己決定的。
              [path moveToPoint:btn.center];
          }else {// 不是selectBtnArray數(shù)組中的第0個(gè)元素
              // 如果不是起點(diǎn),說(shuō)明起點(diǎn)在之前已經(jīng)確定了,我們只需要?jiǎng)澗€到第1個(gè)對(duì)象按鈕。同樣,這第一個(gè)對(duì)象按鈕也是9個(gè)按鈕中的任意一個(gè)
              [path addLineToPoint:btn.center];//1
          }
          //區(qū)分1和2:1在手指拖動(dòng)的過(guò)程中不顯示線,拖動(dòng)到按鈕身上,線才顯示。2在手指在拖動(dòng)的過(guò)程中,邊拖動(dòng)邊顯示 線。巧:2是對(duì)1的補(bǔ)充
      }
    
      //添加一根線到當(dāng)前手指所在的點(diǎn)
      //[path addLineToPoint:self.curP];//2     touchesMoved方法中保存(self.curP = curP;)了手指在移動(dòng)過(guò)程中的所有點(diǎn),并利用 [self setNeedsDisplay]方法,實(shí)際是底層調(diào)用了drawRect:方法。所以在2中可以拿到手指在移動(dòng)過(guò)程中的所有點(diǎn),最后一個(gè)點(diǎn)肯定是當(dāng)前的點(diǎn),倒數(shù)第二個(gè)點(diǎn)相對(duì)于倒數(shù)第三個(gè)點(diǎn)來(lái)說(shuō),就是當(dāng)前的點(diǎn),以此類(lèi)推,利用[path addLineToPoint:self.curP];將線添加到當(dāng)前手指所在的點(diǎn)(手指在拖動(dòng)的過(guò)程中,實(shí)際是有很多點(diǎn),再加上后面的點(diǎn)相對(duì)于前面的點(diǎn)來(lái)說(shuō)始終是當(dāng)前的點(diǎn),所以最終造成了邊拖動(dòng)邊有線的結(jié)果)
    
      //設(shè)置線的狀態(tài)
      [path setLineWidth:10];
      // 紅色路徑
      [[UIColor redColor] set];
      [path setLineJoinStyle:kCGLineJoinRound];
      // 描邊路徑
      [path stroke];
    

    }

}

  • (void)layoutSubviews {
    [super layoutSubviews];

    CGFloat x = 0;
    CGFloat y = 0;
    CGFloat btnWH = 74;

    int column = 3;
    CGFloat margin = (self.bounds.size.width - column * btnWH) / (column + 1);

    int curColumn = 0;
    int curRow = 0;

    //取出每一個(gè)子控件,設(shè)置frame. self.subviews.count值為9
    for (int i = 0 ; i < self.subviews.count; i++) {
    // 九宮格布局。獲取到的列和行本質(zhì)是坐標(biāo)(0,0)(0,1)(0,2)(1,0)(1,1)(1,2)(2,0)(2,1)(2,1)

      //當(dāng)前所在的列
      curColumn = i % column;
      //當(dāng)前所在的行
      curRow = i / column;
    
      x = margin + (margin + btnWH) * curColumn;
      y = margin + (margin + btnWH) * curRow;
    
      //取出每一按鈕
      UIButton *btn = self.subviews[i];
      // 確定每個(gè)按鈕的frame
      btn.frame = CGRectMake(x, y, btnWH, btnWH);
    

    }

}
@end
關(guān)鍵函數(shù)
CGRectContainsPoint(btn.frame, point)// 判斷點(diǎn)point是否在btn中

101.138.gif
畫(huà)板

iOS9 出現(xiàn)的Stack View控件,實(shí)現(xiàn)放置在Stack View內(nèi)部的子控件的自動(dòng)布局,相比于手動(dòng)對(duì)每個(gè)控件設(shè)置約束,Stack View更加效率和精準(zhǔn)。
ViewController.m文件

import "ViewController.h"

import "DrawView.h"

@interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate>

@property (weak, nonatomic) IBOutlet DrawView *drawView;

@end

@implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

}
//屬于誰(shuí)的事, 誰(shuí)來(lái)做
//清屏

  • (IBAction)clear:(id)sender {
    [self.drawView clear];
    }
    //撤銷(xiāo)
  • (IBAction)undo:(id)sender {
    [self.drawView undo];
    }

//橡皮擦

  • (IBAction)erase:(id)sender {
    [self.drawView erase];
    }
    //選擇照片

  • (IBAction)photo:(id)sender {

    UIImagePickerController *pickVC = [[UIImagePickerController alloc] init];
    //設(shè)置照片來(lái)源
    pickVC.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;

    //設(shè)置代理
    pickVC.delegate = self;

    [self presentViewController:pickVC animated:YES completion:nil];

}

//#pa - mark UIImagePickerControllerDelegate

  • (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {

    NSLog(@"%@",info);
    UIImage *image = info[UIImagePickerControllerOriginalImage];
    // NSData *data = UIImagePNGRepresentation(image);
    // [data writeToFile:@"/Users/xiaomage/Desktop/image.png" atomically:YES];
    //
    self.drawView.image = image;

    [self dismissViewControllerAnimated:YES completion:nil];
    }

//保存

  • (IBAction)save:(id)sender {

    //對(duì)畫(huà)板作截屏
    //1.開(kāi)啟一個(gè)位圖上下文
    UIGraphicsBeginImageContext(self.drawView.bounds.size);
    //2.把畫(huà)板的內(nèi)容渲染到上下文當(dāng)中.
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [self.drawView.layer renderInContext:ctx];
    //3.從上下文當(dāng)中取出一張圖片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    //4.關(guān)閉上下文
    UIGraphicsEndImageContext();
    //5.把生成的圖片寫(xiě)入到系統(tǒng)相冊(cè)當(dāng)中
    //注意:寫(xiě)放完成時(shí)調(diào)用的方法必須得是didFinishSavingWithError;
    UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

}

//當(dāng)寫(xiě)入完成時(shí)調(diào)用

  • (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSLog(@"%s",func);
    }

  • (void)success {

}
//設(shè)置線寬度

  • (IBAction)setLineWith:(UISlider *)sender {

    [self.drawView setLineWidth:sender.value];

}
//設(shè)置線的顏色

  • (IBAction)setLineColor:(UIButton *)sender {
    [self.drawView setLineColor:sender.backgroundColor];
    }

  • (BOOL)prefersStatusBarHidden {
    return YES;
    }

@end
DrawView.h文件

import <UIKit/UIKit.h>

@interface DrawView : UIView

//清屏

  • (void)clear;
    //撤銷(xiāo)
  • (void)undo;
    //橡皮擦
  • (void)erase;
    //設(shè)置線寬度
  • (void)setLineWidth:(CGFloat)width;
    //設(shè)置線的顏色
  • (void)setLineColor:(UIColor *)color;

@property (nonatomic ,strong) UIImage *image;

@end
DrawView.m文件

import "DrawView.h"

import "MyBezierPath.h"

@interface DrawView()

/** <#注釋#>*/
@property (nonatomic ,strong) UIBezierPath *path;

/** <#注釋#>*/
@property (nonatomic ,strong) NSMutableArray *pathArray;

@property (nonatomic , assign) CGFloat width;

/** <#注釋#>*/
@property (nonatomic ,strong) UIColor *color;

@end

@implementation DrawView

  • (void)setImage:(UIImage *)image {
    _image = image;
    [self.pathArray addObject:image];
    //重繪
    [self setNeedsDisplay];
    }

//清屏

  • (void)clear {
    //清空所有的路徑
    [self.pathArray removeAllObjects];
    //重繪
    [self setNeedsDisplay];
    }
    //撤銷(xiāo)

  • (void)undo {
    //刪除最后一個(gè)路徑
    [self.pathArray removeLastObject];
    //重繪
    [self setNeedsDisplay];
    }
    //橡皮擦

  • (void)erase {

    [self setLineColor:[UIColor whiteColor]];
    }

//設(shè)置線寬度

  • (void)setLineWidth:(CGFloat)width {

    self.width = width;
    }
    ////設(shè)置線的顏色

  • (void)setLineColor:(UIColor *)color {
    self.color = color;
    }

  • (NSMutableArray *)pathArray {

    if (_pathArray == nil) {
    _pathArray = [NSMutableArray array];
    }
    return _pathArray;
    }

  • (void)awakeFromNib {

    //添加手勢(shì)
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [self addGestureRecognizer:pan];

    self.width = 1;
    // 將黑色存儲(chǔ)到color屬性中
    self.color = [UIColor blackColor];
    }

  • (void)pan:(UIPanGestureRecognizer *)pan {

    //畫(huà)線
    //獲取當(dāng)前手指的點(diǎn)
    CGPoint curP = [pan locationInView:self];
    if (pan.state == UIGestureRecognizerStateBegan) {
    //創(chuàng)建路徑
    //如果發(fā)現(xiàn)系統(tǒng)的類(lèi)型沒(méi)有辦法瞞足要求時(shí),自定義類(lèi).繼承原來(lái)的類(lèi),在原來(lái)類(lèi)的基礎(chǔ)上,添加屬于自己的東西.
    MyBezierPath *path = [[MyBezierPath alloc] init];
    [path setLineWidth:self.width];
    [path setLineJoinStyle:kCGLineJoinRound];
    [path setLineCapStyle:kCGLineCapRound];
    // 取出等號(hào)右邊color存儲(chǔ)的顏色賦值給左邊的color(外界如果設(shè)置了顏色,那么會(huì)覆蓋掉黑色)
    path.color = self.color;
    // 將path作為全局變量,供別的方法訪問(wèn)
    self.path = path;
    //path這個(gè)對(duì)象是個(gè)空的,應(yīng)該放到第二個(gè)if里面吧。
    [self.pathArray addObject:path];
    [path moveToPoint:curP];
    } else if (pan.state == UIGestureRecognizerStateChanged) {
    //畫(huà)的應(yīng)該為直線,為什么是任意的線(也能畫(huà)曲線)?你這句話是錯(cuò)誤的。因?yàn)槟憬oDrawView添加了手勢(shì),所以畫(huà)的每一個(gè)點(diǎn)都會(huì)被記錄到,兩點(diǎn)之間確實(shí)是直線,但是如果任意兩個(gè)相鄰的點(diǎn)的距離都是1像素的話,每條線稍微彎折一點(diǎn)點(diǎn),那么1000個(gè)點(diǎn)肯定能形成平滑的曲線
    [self.path addLineToPoint:curP];
    //將手勢(shì)+貝塞爾曲線得到的每一個(gè)點(diǎn)繪制成路徑(本質(zhì)調(diào)用drawRect:)
    [self setNeedsDisplay];
    }

}
// 繪制路徑

  • (void)drawRect:(CGRect)rect {

    //繪制出保存的所有路徑
    for (MyBezierPath *path in self.pathArray) {

      if ([path isKindOfClass:[UIImage class]]) {
          UIImage *image = (UIImage *)path;
          // 在圖片中繪制路徑
          [image drawInRect:rect];
      }else {
          [path.color set];
          [path stroke];
      }
    

    }

}

@end
MyBezierPath.h文件

import <UIKit/UIKit.h>

@interface MyBezierPath : UIBezierPath
@property (nonatomic ,strong) UIColor *color;

@end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,011評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,263評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,543評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,323評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,874評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,095評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,605評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,551評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,720評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,961評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,358評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,612評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,330評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,690評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • Quartz2D 簡(jiǎn)介及用途 Quartz 2D 是一個(gè)二維繪圖引擎,同時(shí)支持iOS和Mac系統(tǒng),Quartz2D...
    45b645c5912e閱讀 974評(píng)論 1 16
  • 什么是Quartz2D 是一個(gè)二維的繪圖引擎,同時(shí)支持iOS和Mac系統(tǒng) Quartz2D的API是純C語(yǔ)言的,它...
    Mario_ZJ閱讀 595評(píng)論 0 1
  • 簡(jiǎn)述: 1、Quartz2D是什么Quartz2D是二維繪圖引擎,同時(shí)支持IOS和Mac 2、Quartz2D能做...
    LitterL閱讀 648評(píng)論 0 6
  • Quartz2D 簡(jiǎn)介 Quartz2D是二維(平面)的繪圖引擎(經(jīng)包裝的函數(shù)庫(kù),方便開(kāi)發(fā)者使用。也就是說(shuō)蘋(píng)果幫我...
    iOS_Cqlee閱讀 636評(píng)論 0 2
  • 前提安裝好cocoaPods 1.測(cè)試你的安裝是否正確 $:sudo gem install cocoapods...
    妮妮世界閱讀 345評(píng)論 0 1