Quartz2D

Quartz2D以及drawRect的重繪機制
字數1487 閱讀21 評論1 喜歡1
一、什么是Quartz2D

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

三、Quartz2D在iOS開發中的價值

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

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

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

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

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

1.獲得圖形上下文

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

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

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

新建一個起點

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

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

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

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

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

一般以CGContextDraw、CGContextStroke、CGContextFill開頭的函數,都是用來繪制路徑的
Mode參數決定繪制的模式

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

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

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

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

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

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

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

畫矩形、正方形

  • (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.把上下文的內容渲染到View的layer.
    // CGContextStrokePath(ctx);// 描邊路徑
    CGContextFillPath(ctx);// 填充路徑

}

描邊矩形.png

填充矩形.png
畫扇形

  • (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;
    // 畫弧的路徑
    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
畫圓形

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

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

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

}

描邊圓形.png

填充圓形.png
畫圓角矩形

  • (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.把上下文的內容渲染到View的layer.
    CGContextStrokePath(ctx);// 描邊路徑
    // CGContextFillPath(ctx);// 填充路徑

}

描邊圓角矩形.png

填充圓角矩形.png
畫直線

  • (void)drawRect:(CGRect)rect {

    //1.獲取跟View相關聯的上下文(uigraphics開頭)
    CGContextRef ctx = UIGraphicsGetCurrentContext();

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

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

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

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

}

線條.png

相交線條.png
畫不規則圖形+添加點擊事件

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

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

//設置描邊色
[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 {

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

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

}

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

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

@end

不規則圖形+點擊事件.gif
畫曲線

本塞爾曲線原理

貝塞爾曲線.gif

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

}

曲線.png
畫餅圖

做法1:

  • (void)drawRect:(CGRect)rect {
    NSArray *dataArray = @[@25,@25,@50];
    // 畫弧
    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;
    // 遍歷出第一個對象25,angle =25/100 *2π,即angle = π/2,所以為1/4圓,
    angle = num.intValue / 100.0 * M_PI * 2;
    // 截至角度= 開始的角度+ 遍歷出的對象所占整個圓形的角度
    endA = startA + angle;
    // 順勢針畫貝塞爾曲線
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
    // 設置隨機顏色
    [[self randomColor] set];
    // 添加一根線到圓心
    [path addLineToPoint:center];
    // 填充路徑
    // [path fill];
    // 描邊路徑
    [path stroke];
    }

}

描邊餅圖.png

填充餅圖.png

  • (void)drawRect:(CGRect)rect {
    NSArray *dataArray = @[@25,@25,@50];
    // 畫弧
    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;
      // 遍歷出第一個對象25,angle =25/100 *2π,即angle = π/2,所以為1/4圓,
      angle = num.intValue / 100.0 * M_PI * 2;
      // 截至角度= 開始的角度+ 遍歷出的對象所占整個圓形的角度
      endA = startA + angle;
      // 順勢針畫貝塞爾曲線
      UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
      // 設置隨機顏色
      [[self randomColor] set];
      // 添加一根線到圓心
      [path addLineToPoint:center];
      // 填充路徑
     [path fill];
      // 描邊路徑
    

// [path stroke];
}
}

//隨機生成一個顏色

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

    //第二個扇形
    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];}

//隨機生成一個顏色

  • (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
畫文字

  • (void)drawRect:(CGRect)rect {
    NSString *str = @"簡書:CoderZb";

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

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

}

文字.png
模擬系統UIImageView是如何畫出圖片的
底層調用了 [self setNeedsDisplay];進行重繪,接著調用drawRect:畫出圖片

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 {

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

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

    //想要讓CADisplayLink工作, 必須得要添加到運行循環當中.
    [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

//setNeedsDisplay底層會調用drawRect,并不是立馬調用的.只是設了一個調用的標志.調用時刻是等下一次屏幕刷新時才去調用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

畫圖片.png
矩陣之縮放、移動、旋轉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);
    //移動
    CGContextTranslateCTM(ctx, 150,200);

    //旋轉
    CGContextRotateCTM(ctx, M_PI_4);

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

}

原始.png

縮放.png

移動.png

旋轉.png
雪花+CA定時器+drawRect

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

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

    //想要讓CADisplayLink工作, 必須得要添加到運行循環當中.
    [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

//setNeedsDisplay底層會調用drawRect,并不是立馬調用的.只是設了一個調用的標志.調用時刻是等下一次屏幕刷新時才去調用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
畫水印

import "ViewController.h"

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

@end

@implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

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

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

[str drawAtPoint:CGPointZero withAttributes:dict];
//4.從上下文當中生成一張圖片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//5.關閉位圖上下文
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.開啟位圖上下文(大小 原始圖片的寬高度+ 2 *邊框寬度)
    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.繪制小圓(把小圓設置成裁剪區域)
    UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(borderW, borderW, oriImage.size.width, oriImage.size.height)];
    [clipPath addClip];
    //6.把圖片繪制到上下文當中
    [oriImage drawAtPoint:CGPointMake(borderW, borderW)];
    //7.從上下文當中生成圖片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    //8.關閉上下文.
    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.開啟一個位圖上下文
UIGraphicsBeginImageContext(self.view.bounds.size);
//2.把View的內容繪制到上下文當中
CGContextRef ctx =  UIGraphicsGetCurrentContext();
//UIView內容想要繪制到上下文當中, 必須使用渲染的方式
[self.view.layer renderInContext:ctx];
//3.從上下文當中生成一張圖片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//4.關閉上下文
UIGraphicsEndImageContext();
//把圖片轉成二進制流
//NSData *data = UIImageJPEGRepresentation(newImage, 1);
NSData *data = UIImagePNGRepresentation(newImage);

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

}

@end

101.125.gif
配合手勢裁剪圖片

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) {
    //創建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 {

    //獲取當前手指所在的點
    CGPoint curP = [pan locationInView:self.imageV];
    // 判斷手勢的狀態
    // 要執行2,那么1必須執行過。聯想一下點擊圖片的過程就理解了。
    // 要執行3,那么1,2也必須執行過。
    if(pan.state == UIGestureRecognizerStateBegan) {// 1
    //記錄當前手指的開始點
    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);

    //設置裁剪區域
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.coverView.frame];
    [path addClip];

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

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

    //關閉上下文
    UIGraphicsEndImageContext();

    self.imageV.image = newImage;

    [self.coverView removeFromSuperview];
}

}
@end

101.126.gif
配合手勢擦除圖片

import "ViewController.h"

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

@end

@implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];

    self.imageV.userInteractionEnabled = YES;

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

}

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

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

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



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

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

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

//關閉上下文
UIGraphicsEndImageContext();

}

@end

101.127.gif
配合UITouch實現手勢解鎖鏈接密碼

部分代碼:

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++) {

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

    }
    }

//按功能模塊抽方法
//獲取當前手指的點

  • (CGPoint)getCurPoint:(NSSet )touches {//CGPoint后面沒有,強烈注意
    //1獲取當前手指的點

    //1.1當用戶用一根手指觸摸屏幕時,會創建一個與手指相關聯的UITouch對象
    UITouch *touch = [touches anyObject];
    //1.2手指在當前view上的位置
    CGPoint curP = [touch locationInView:self];
    return curP;
    }

//給定一個點,判斷這個點在不在按鈕身上
//如果沒有找到符合的條件,直接返回nil.

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

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

    }
    return nil;
    }

//手指開始點擊

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

}

//手指移動

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

    //獲取當前手指的點
    CGPoint curP = [self getCurPoint:touches];

    //在手指移動的過程中,邊移動手指,邊用self.curP保存當前手指的點
    self.curP = curP;

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

    //重繪
    [self setNeedsDisplay];
    }

//手指離開

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

NSMutableString *str = [NSMutableString string];

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

 }

 NSLog(@"%@",str);

// 無論是畫路徑還是清空路徑,必須得調用setNeedsDisplay

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

//清空路徑還必須調用重繪方法
[self setNeedsDisplay];

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

}

  • (void)drawRect:(CGRect)rect {

    if (self.selectBtnArray.count) {//view開始顯示之前會調用drawRect方法,此時方法里面沒有任何值,數組里面也沒有值,所以要先判斷,if (self.selectBtnArray.count),如果沒有值,就不執行if里面的內容

      //描述路徑
      UIBezierPath *path = [UIBezierPath bezierPath];
      //取出所有選中的按鈕
    
      for (int i = 0; i < self.selectBtnArray.count; i++) {
          //取出每一個按鈕
          UIButton *btn =  self.selectBtnArray[i];
    
          if (i == 0) {// selectBtnArray數組中的第0個對象
              //讓selectBtnArray數組中的第0個對象按鈕成為路徑的起點。第0個對象是9個按鈕中的任意一個,因為點擊哪個按鈕由用戶自己決定的。
              [path moveToPoint:btn.center];
          }else {// 不是selectBtnArray數組中的第0個元素
              // 如果不是起點,說明起點在之前已經確定了,我們只需要劃線到第1個對象按鈕。同樣,這第一個對象按鈕也是9個按鈕中的任意一個
              [path addLineToPoint:btn.center];//1
          }
          //區分1和2:1在手指拖動的過程中不顯示線,拖動到按鈕身上,線才顯示。2在手指在拖動的過程中,邊拖動邊顯示 線。巧:2是對1的補充
      }
    
      //添加一根線到當前手指所在的點
      //[path addLineToPoint:self.curP];//2     touchesMoved方法中保存(self.curP = curP;)了手指在移動過程中的所有點,并利用 [self setNeedsDisplay]方法,實際是底層調用了drawRect:方法。所以在2中可以拿到手指在移動過程中的所有點,最后一個點肯定是當前的點,倒數第二個點相對于倒數第三個點來說,就是當前的點,以此類推,利用[path addLineToPoint:self.curP];將線添加到當前手指所在的點(手指在拖動的過程中,實際是有很多點,再加上后面的點相對于前面的點來說始終是當前的點,所以最終造成了邊拖動邊有線的結果)
    
      //設置線的狀態
      [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;

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

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

    }

}
@end
關鍵函數
CGRectContainsPoint(btn.frame, point)// 判斷點point是否在btn中

101.138.gif
畫板

iOS9 出現的Stack View控件,實現放置在Stack View內部的子控件的自動布局,相比于手動對每個控件設置約束,Stack View更加效率和精準。
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];

}
//屬于誰的事, 誰來做
//清屏

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

//橡皮擦

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

  • (IBAction)photo:(id)sender {

    UIImagePickerController *pickVC = [[UIImagePickerController alloc] init];
    //設置照片來源
    pickVC.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;

    //設置代理
    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 {

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

}

//當寫入完成時調用

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

  • (void)success {

}
//設置線寬度

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

    [self.drawView setLineWidth:sender.value];

}
//設置線的顏色

  • (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;
    //撤銷
  • (void)undo;
    //橡皮擦
  • (void)erase;
    //設置線寬度
  • (void)setLineWidth:(CGFloat)width;
    //設置線的顏色
  • (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];
    }
    //撤銷

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

  • (void)erase {

    [self setLineColor:[UIColor whiteColor]];
    }

//設置線寬度

  • (void)setLineWidth:(CGFloat)width {

    self.width = width;
    }
    ////設置線的顏色

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

  • (NSMutableArray *)pathArray {

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

  • (void)awakeFromNib {

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

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

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

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • Quartz2D 簡介及用途 Quartz 2D 是一個二維繪圖引擎,同時支持iOS和Mac系統,Quartz2D...
    45b645c5912e閱讀 976評論 1 16
  • 什么是Quartz2D 是一個二維的繪圖引擎,同時支持iOS和Mac系統 Quartz2D的API是純C語言的,它...
    Mario_ZJ閱讀 599評論 0 1
  • 簡述: 1、Quartz2D是什么Quartz2D是二維繪圖引擎,同時支持IOS和Mac 2、Quartz2D能做...
    LitterL閱讀 648評論 0 6
  • Quartz2D 簡介 Quartz2D是二維(平面)的繪圖引擎(經包裝的函數庫,方便開發者使用。也就是說蘋果幫我...
    iOS_Cqlee閱讀 636評論 0 2
  • 前提安裝好cocoaPods 1.測試你的安裝是否正確 $:sudo gem install cocoapods...
    妮妮世界閱讀 345評論 0 1