15~16day-Quartz2D(自定義控件、圖片水印、裁剪以及屏幕截圖)


前言

掌握
1、- (void)drawRect:(CGRect)rect;的使用
2、常見圖形的繪制
3、繪圖狀態的設置:文字顏色、線寬
4、圖形上下文狀態的保持、恢復
5、圖形上下文
6、矩陣操作
7、quartz 2D 的內存管理
8、圖片水印、裁剪以及屏幕的截圖

一、什么是quartz 2D

quartz 2D是一個二維繪圖引擎,同時支持iOS、Mac系統

1、quartz 2D能完成的工作
1》繪制圖形、文字
2》繪制、生成圖片(圖像)
3》讀取、生成PDF文件
4》圖片的裁截:圓形裁剪
5》自定義控件
2、quartz 2D在iOS開發中的價值
當使用uikit框架的普通控件無法實現需求的時候,可采用quartz 2D技術將 控件內部的結構畫出來 ,自定義UI控件的樣子

3、 Quartz2D須知

1)Quartz2D的API是純C語言的
2)Quartz2D的API來自於Core Graphics框架
3)數據類型和函數基本都以CG作為前綴:CGContextRef、CGPathRef、CGContextStrokePath(ctx);
4、 Quartz 2D繪圖的基礎元素——路徑

1)路徑定義了一條或者或多條形狀或子路徑
2)子路徑可以包含一條或者多條直線或曲線
3)子路徑也可以是一些簡單的形狀,例如線、圓形、矩形或者星型等
4)子路徑還可以包含復雜的形狀,例如地圖輪廓或者塗鴉等
5)路徑可以是開放的,也可以是封閉的
6)路徑主要使用在定義視圖運動軌跡
4、quartz 2D 的內存管理
1、如果含有create、copy的函數創建對象,使用完之后必須釋放,否則將導致內測泄露
2、如果retain了一個對象,不在使用時需將其release掉
1》可以使用quart 2D的函數(e g. CGColorSpaceRetain)來指定retain\release一個對象,或者使用core foundation 的CFRetain.

二、圖形上下文(graphics context)

是一個CGContextRef類型數據
1、作用
1》保持繪圖的信息
2》決定繪圖的輸出目標(PDF、bitmap、layer、printer、window)

這里寫圖片描述
p s:相同的一套繪圖序列,指定不同的Graphics Context,就可將相同的圖像繪制到不同的目標上

2、分類
Quartz2D提供了以下幾種類型的Graphics Context:

Bitmap Graphics Context
PDF Graphics Context
Window Graphics Context
Layer Graphics Context
Printer Graphics Context

3、 圖形上下文棧的操作

//將當前的上下文copy一份,保存到棧頂(那個棧叫做”圖形上下文棧”)
void CGContextSaveGState(CGContextRef c)
//將棧頂的上下文出棧,替換掉當前的上下文
void CGContextRestoreGState(CGContextRef c)
- (void)drawRect:(CGRect)rect{

CGContextRef context= UIGraphicsGetCurrentContext();

//Pushes a copy of the current graphics state onto the graphics state stack for the context.

CGContextSaveGState(context);

//設置繪制信息

UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(10, 125)];

[path addLineToPoint:CGPointMake(240, 125)];

CGContextAddPath(context, path.CGPath);

//設置繪圖狀態

[[UIColor redColor] set];

//渲染

CGContextStrokePath(context);

//繪制第二條線

UIBezierPath *path1 = [UIBezierPath bezierPath];

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

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



CGContextAddPath(context, path1.CGPath);//Adds a previously created Quartz path object to the current path in a graphics context.

//Sets the current graphics state to the state most recently saved.設置繪圖狀態為最近保存的上下文狀態

CGContextRestoreGState(context);

CGContextStrokePath(context);

}

三、自定義控件

1、如何利用Quartz2D繪制東西到view上?
(1)首先,得有圖形上下文,因為它能保存繪圖信息,並且決定着繪制到什么地方去
(2)其次,那個圖形上下文必須跟view相關聯,才能將內容繪制到view上面
2、自定義UI控件的步驟
1)新建一個類,繼承自UIView
2)實現- (void)drawRect:(CGRect)rect方法,然后在這個方法中,可以:
(1)取得跟當前view相關聯的圖形上下文
(2)繪制相應的圖形內容,繪制時產生的線條稱為路徑。 路徑由一個或多個直線段或曲線段組成。
(3)利用圖形上下文將繪制的所有內容渲染顯示到view上面
也可以:利用UIKit封裝的繪圖函數直接繪圖
3、drawRect:方法在什么時候被調用?
(1)當view第一次顯示到屏幕上時(被加到UIWindow上顯示出來)
(2)調用view的setNeedsDisplay或者setNeedsDisplayInRect:時
4、 drawRect:中取得的上下文(Layer Graphics Context)

在drawRect:方法中取得上下文后,就可以繪制東西到view上
1)View內部有個layer(圖層)屬性,drawRect:方法中取得的是一個Layer Graphics Context,因此,繪制的東西其實是繪制到view的layer上去了
2)View之所以能顯示東西,完全是因為它內部的layer

四、quartz 2D 繪圖的代碼步驟

1.獲得圖形上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

2.拼接路徑(下面代碼是搞一條線段)


//新建一個起點
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)

3.繪制路徑

//Mode參數決定繪制的模式
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
//繪制空心路徑
void CGContextStrokePath(CGContextRef c)
//繪制實心路徑
void CGContextFillPath(CGContextRef c)

提示:一般以CGContextDraw、CGContextStroke、CGContextFill開頭的函數,都是用來繪制路徑的

4、案例 繪制下載進度條
這里寫圖片描述

#import "HSProgressView.h"




@interface HSProgressView ()

@property (nonatomic,weak) UILabel *textLable;




@end




@implementation HSProgressView




- (UILabel *)textLable{

if (nil == _textLable) {

UILabel *tmpView = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];

_textLable = tmpView;

[_textLable setTextAlignment:NSTextAlignmentCenter];

[self addSubview:_textLable];

}

return _textLable;

}




//重寫百分比的setter方法,來做一些額外的事情




- (void)setProgressValue:(CGFloat)progressValue{

_progressValue = progressValue;

[self.textLable setText:[NSString stringWithFormat:@"%.2f%%",progressValue*100]];

//繪制進度圖形
/**
在view 上做重繪的標志,當下次屏幕刷新的時候會調用drawRect方法

*/


[self setNeedsDisplay];//重繪視圖to notify the system that your view’s contents need to be redrawn.

}




/*繪制進度*/

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

// Drawing code

CGContextRef context = UIGraphicsGetCurrentContext();

CGPoint center = CGPointMake(50, 50);

CGFloat radious = 44;

CGFloat startAngle = -M_PI_2;

CGFloat endAngle = startAngle + self.progressValue * M_PI*2;

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radious startAngle:startAngle endAngle:endAngle clockwise:YES];

CGContextAddPath(context, path.CGPath);

//渲染

CGContextStrokePath(context);
}

五、圖片水印、裁剪以及屏幕截圖

1、圖片水印

//1、開啟一個基於位圖的圖形上下文
void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale);

//2.從上下文中取得圖片(UIImage)

UIImage* UIGraphicsGetImageFromCurrentImageContext();

//3.結束基於位圖的圖形上下文
void UIGraphicsEndImageContext();
/*例子*/
//1、開啟上下文

UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);

UIImage *image = [UIImage imageNamed:@"img"];

[image drawAtPoint:CGPointZero];

[@"@Lydia" drawAtPoint:CGPointMake(150, 150) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12],NSForegroundColorAttributeName:[UIColor redColor]}];

//2、獲取上下文圖片

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

//3、結束上下文

UIGraphicsEndImageContext();

//4、顯示圖片

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];

[imageView setImage:newImage];

[self.view addSubview:imageView];

//5、保存圖片

// NSData *data = UIImageJPEGRepresentation(newImage, 0.0001);// value 1.0 represents the least compression (or best quality)

NSData *data = UIImagePNGRepresentation(newImage);//將圖片轉換成二進制數據

[data writeToFile:@"/Users/devzkn/Desktop/lydia.png" atomically:YES];

2、圖片裁剪

這里寫圖片描述

void CGContextClip(CGContextRef c);//將當前上下所繪制的路徑裁剪出來(超出這個裁剪區域的都不能顯示)
//方式二、裁剪感興趣的部分

UIRectClip(textFrame);//Modifies the current clipping path by intersecting it with the specified rectangle.
/*裁剪代碼*/
#import "UIImage+Tool.h"

@implementation UIImage (Tool)

+ (UIImage *)imageWithName:(NSString *)name border:(CGFloat)border borderColor:(UIColor *)borderColor{

UIImage *image = [UIImage imageNamed:name];//舊圖片

CGFloat borderWidth = border;//圓環高度

//新圖片的大小

CGFloat newImageW = image.size.width+borderWidth*2;

CGFloat newImageH = image.size.height+borderWidth*2;

CGFloat circleW = (newImageH>newImageW) ? newImageW:newImageH;//新的圖片尺寸



//1、開啟上下文

UIGraphicsBeginImageContextWithOptions(CGSizeMake(circleW, circleW), NO, 0.0);

//1>畫大圓正切於矩形-----------

UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, circleW,circleW)];

//獲取當前上下文

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextAddPath(context, path.CGPath);

[borderColor set];

CGContextFillPath(context);

//繪制小圓

//2>畫一個正切於舊圖片的小圓-----

CGRect clipR = CGRectMake(borderWidth,borderWidth,image.size.width,image.size.height);

UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:clipR];

[clipPath addClip];//設置裁剪區域

[image drawAtPoint:CGPointMake(borderWidth, borderWidth)];

//2、獲取上下文圖片

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();



//3、結束上下文

UIGraphicsEndImageContext();

return newImage;

}

3、屏幕截圖

核心代碼:調用某個view的layer的renderInContext:方法即可
- (void)renderInContext:(CGContextRef)ctx;
UIGraphicsBeginImageContext(self.view.bounds.size);

CGContextRef context = UIGraphicsGetCurrentContext();

//將layer渲染到上下文

[self.view.layer renderInContext:context];

//獲取新圖片

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

NSData *data = UIImagePNGRepresentation(newImage);

[data writeToFile:@"/Users/devzkn/Desktop/layer.png" atomically:YES];

正文

一、案例
1、餅圖

這里寫圖片描述

/*pie

尋找規律

*/


// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

// Drawing code

CGContextRef context = UIGraphicsGetCurrentContext();

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

CGPoint center = CGPointMake(125, 125);

CGFloat radious = 100;//半徑

CGFloat startAngle = 0;//起始弧度

CGFloat endAngle = 0;

CGFloat angle = 0;

//開始繪制餅圖

for (NSNumber *num in data) {

//繪制

angle =num.floatValue/100.0 *M_PI*2;

startAngle = endAngle;//上一個餅圖的結束弧度為下一個餅圖的開始弧度

endAngle = startAngle + angle;

UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radious startAngle:startAngle endAngle:endAngle clockwise:YES];

[path2 addLineToPoint:center];

CGContextAddPath(context, path2.CGPath);

[[UIColor randomColor]set];

//渲染第i個

CGContextFillPath(context);

}



}




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

[self setNeedsDisplay];

}

//生成隨機顏色
+ (UIColor *)randomColor{

/*

RGB 24位,RGB 每個顏色通道8位,8 的二進制255,即顏色取值是0~255

RGBA

*/


CGFloat red = arc4random_uniform(256)/255.0;

CGFloat blue = arc4random_uniform(256)/255.0;

CGFloat green = arc4random_uniform(256)/255.0;

return [UIColor colorWithRed:red green:green blue:blue alpha:1];

}

2、柱狀圖
這里寫圖片描述

/* bar*/

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

// Drawing code

CGContextRef context = UIGraphicsGetCurrentContext();

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

CGFloat ViewH =CGRectGetHeight(self.frame);

CGFloat width = CGRectGetWidth(self.frame)/(2*data.count-1);

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

//繪制

CGFloat height = ViewH*([(NSNumber *)data[i] floatValue]/100.0);

CGFloat x = i*width*2;

CGFloat y = ViewH - height ;

UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, width, height)];

CGContextAddPath(context, path.CGPath);

[[UIColor randomColor] set];

//渲染

CGContextFillPath(context);

}

}

3、UIKit的演練

1》- (void)drawInRect:(CGRect)rect withAttributes:(NSDictionary<NSString *,id> *)attrs
/*

字符屬性



字符屬性可以應用於 attributed string 的文本中。



NSString *const NSFontAttributeName;(字體)



NSString *const NSParagraphStyleAttributeName;(段落)



NSString *const NSForegroundColorAttributeName;(字體顏色)



NSString *const NSBackgroundColorAttributeName;(字體背景色)



NSString *const NSLigatureAttributeName;(連字符)



NSString *const NSKernAttributeName;(字間距)



NSString *const NSStrikethroughStyleAttributeName;(刪除線)



NSString *const NSUnderlineStyleAttributeName;(下划線)



NSString *const NSStrokeColorAttributeName;(邊線顏色)



NSString *const NSStrokeWidthAttributeName;(邊線寬度)



NSString *const NSShadowAttributeName;(陰影)(橫豎排版)



NSString *const NSVerticalGlyphFormAttributeName;



常量



1> NSFontAttributeName(字體)



該屬性所對應的值是一個 UIFont 對象。該屬性用於改變一段文本的字體。如果不指定該屬性,則默認為12-point Helvetica(Neue)。



2> NSParagraphStyleAttributeName(段落)



該屬性所對應的值是一個 NSParagraphStyle 對象。該屬性在一段文本上應用多個屬性。如果不指定該屬性,則默認為 NSParagraphStyle 的defaultParagraphStyle 方法返回的默認段落屬性。



3> NSForegroundColorAttributeName(字體顏色)



該屬性所對應的值是一個 UIColor 對象。該屬性用於指定一段文本的字體顏色。如果不指定該屬性,則默認為黑色。



4> NSBackgroundColorAttributeName(字體背景色)



該屬性所對應的值是一個 UIColor 對象。該屬性用於指定一段文本的背景顏色。如果不指定該屬性,則默認無背景色。



5> NSLigatureAttributeName(連字符)



該屬性所對應的值是一個 NSNumber 對象(整數)。連體字符是指某些連在一起的字符,它們采用單個的圖元符號。0 表示沒有連體字符。1 表示使用默認的連體字符。2表示使用所有連體符號。默認值為 1(注意,iOS 不支持值為 2)。



6> NSKernAttributeName(字間距)



該屬性所對應的值是一個 NSNumber 對象(整數)。字母緊排指定了用於調整字距的像素點數。字母緊排的效果依賴於字體。值為 0 表示不使用字母緊排。默認值為0



7> NSStrikethroughStyleAttributeName(刪除線)



該屬性所對應的值是一個 NSNumber 對象(整數)。該值指定是否在文字上加上刪除線,該值參考“Underline Style Attributes”。默認值是NSUnderlineStyleNone。



8> NSUnderlineStyleAttributeName(下划線)



該屬性所對應的值是一個 NSNumber 對象(整數)。該值指定是否在文字上加上下划線,該值參考“Underline Style Attributes”。默認值是NSUnderlineStyleNone。



9> NSStrokeColorAttributeName(邊線顏色)



該屬性所對應的值是一個 UIColor 對象。如果該屬性不指定(默認),則等同於 NSForegroundColorAttributeName。否則,指定為刪除線或下划線顏色。更多細節見“Drawing attributedstrings that are both filled and stroked”。



10> NSStrokeWidthAttributeName(邊線寬度)



該屬性所對應的值是一個 NSNumber 對象(小數)。該值改變描邊寬度(相對於字體size 的百分比)。默認為 0,即不改變。正數只改變描邊寬度。負數同時改變文字的描邊和填充寬度。例如,對於常見的空心字,這個值通常為3.0



11> NSShadowAttributeName(陰影)
該屬性所對應的值是一個 NSShadow 對象。默認為 nil。
12> NSVerticalGlyphFormAttributeName(橫豎排版)
該屬性所對應的值是一個 NSNumber 對象(整數)。0 表示橫排文本。1 表示豎排文本。在 iOS 中,總是使用橫排文本,0 以外的值都未定義。
*/

4、雪花

這里寫圖片描述

- (void)drawRect:(CGRect)rect{

if (self.snowY >= CGRectGetHeight(self.frame)) {

self.snowY = 0;

}

self.snowY++;

UIImage *image = [UIImage imageNamed:@"雪花"];

[image drawAtPoint:CGPointMake(0, self.snowY)];

}




- (void)awakeFromNib{

/*

1.NSTimer的精確度就顯得低了點,比如NSTimer的觸發時間到的時候,runloop如果在阻塞狀態,觸發時間就會推遲到下一個runloop周期--》導致任務的疊加。

2.CADisplayLink使用場合相對專一,適合做UI的不停重繪,比如自定義動畫引擎或者視頻播放的渲染。NSTimer的使用范圍要廣泛的多,各種需要單次或者循環定時處理的任務都可以使用。在UI相關的動畫或者顯示內容使用 CADisplayLink比起用NSTimer的好處就是我們不需要在格外關心屏幕的刷新頻率了,因為它本身就是跟屏幕刷新同步的。

*/


// [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];

/*

IOS設備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會在每次刷新結束都被調用,精確度相當高。

*/


CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)];

[link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

}

5、矩陣操作

- (void)drawRect:(CGRect)rect{

CGContextRef context = UIGraphicsGetCurrentContext();

//平移上下文

CGContextTranslateCTM(context, 50, 100);

CGContextRotateCTM(context, M_PI_4);

CGContextScaleCTM(context, 0.2, 0.7);

//上下文矩陣操作,之后在設置繪圖信息(路徑)

UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-50, -100, 120, 100)];

CGContextAddPath(context, path.CGPath);

[[UIColor yellowColor]set];

CGContextStrokePath(context);

}

6、手勢密碼

//

// HSLockView.m

// 20140621-手勢解鎖

//

// Created by devzkn on 4/21/16.

// Copyright © 2016 hisun. All rights reserved.

//




#import "HSLockView.h"




@interface HSLockView ()

@property (nonatomic,strong) NSMutableArray *btns;//存儲選中按鈕

@property (nonatomic,assign) CGPoint pos;//當前手指的位置










@end

@implementation HSLockView

- (NSMutableArray *)btns{

if (nil == _btns) {

_btns = [NSMutableArray array];

}

return _btns;

}




#if 0

- (void)awakeFromNib{

//繪制九宮格

}

#endif




- (instancetype)initWithCoder:(NSCoder *)aDecoder{



self = [super initWithCoder:aDecoder];

if (self) {

//繪制九宮格按鈕

[self addBtns];

}

return self;



}




/**

繪制九宮格

*/


- (void)addBtns{

UIImage *normalImage = [UIImage imageNamed:@"gesture_node_normal"];

UIImage *highlightedImage = [UIImage imageNamed:@"gesture_node_highlighted"];




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

//繪制按鈕

UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];

//設置共性信息

[btn setTag:i];//用於標記按鈕

[btn setImage:normalImage forState:UIControlStateNormal];

[btn setImage:highlightedImage forState:UIControlStateSelected];//選中狀態要手動管理

/**

處理按鈕點擊事件:

方式一:addTarget:self

方式二;touches ,設置按鈕為不可交互,交給VIew進行處理(事件的傳遞原理)

*/


//添加監聽方法,進行性手動管理選中狀態

// [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchDown];

[btn setUserInteractionEnabled:NO];




[self addSubview:btn];

}

}




#pragma mark - 點擊事件

#if 0

- (void)clickBtn:(UIButton*)btn{

[btn setSelected:YES];

}

#endif




#pragma mark - 使用touches方法 進行按鈕的選中處理




/** 獲取當前觸摸點的坐標*/

- (CGPoint) getPosWithTouches:(NSSet *)touches{

if (touches == nil) {

return CGPointZero;

}

UITouch *touch = [touches anyObject];//Returns one of the objects in the set, or nil if the set contains no objects.

return [touch locationInView:self];//當前觸摸點

}




/** 選中按鈕的獲取*/

- (UIButton *) getButtonWithPos:(CGPoint)pos{

//計算選中范圍

CGRect selectRect;

UIButton *posBtn ;

CGFloat wh =30;

for (UIButton *btn in self.subviews) {

CGFloat x = btn.center.x-wh*0.5;

CGFloat y = btn.center.y- wh *0.5;

selectRect = CGRectMake(x, y, wh, wh);

BOOL flg = CGRectContainsPoint(selectRect, pos);

if (flg) {

posBtn = btn;

break;

}

}

return posBtn;

}




#pragma mark - 手指抬起的處理

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

//判斷是否解鎖成功

NSMutableString *answerStr = [[NSMutableString alloc]init];

for (UIButton *btn in self.btns) {

[answerStr appendFormat:@"%d",btn.tag];

}

NSLog(@"%@",answerStr);

//去除選中狀態

[self.btns makeObjectsPerformSelector:@selector(setSelected:) withObject:NO];

//清空軌跡

[self.btns removeAllObjects];

[self setNeedsDisplay];

}




/**控制按鈕的選擇狀態 */

#if 0

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



}

#endif




/** 存儲選中按鈕*/

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

CGPoint pos = [self getPosWithTouches:touches];

[self setPos:pos];

UIButton *btn = [self getButtonWithPos:pos];

if (btn && btn.selected == NO) {

[btn setSelected:YES];

if (![self.btns containsObject:btn]) {

[self.btns addObject:btn];//存儲選中按鈕

}

}

[self setNeedsDisplay];//繪制




}










#pragma mark - 畫線




- (void)drawRect:(CGRect)rect{

if (self.btns.count == 0) {

return;

}

//開始繪制

UIBezierPath *path = [UIBezierPath bezierPath];

for (int i=0; i<self.btns.count; i++) {

UIButton *btn = self.btns[i];

if (i==0) {

[path moveToPoint:btn.center];

}else{

[path addLineToPoint:btn.center];

}

}



[path addLineToPoint:self.pos];

//設置繪圖狀態

[[UIColor greenColor]set];

[path setLineWidth:10];

[path setLineCapStyle:kCGLineCapRound];

[path setLineJoinStyle:kCGLineJoinRound];



[path stroke];

}










#pragma mark - layoutSubviews 設置子視圖的位置信息




- (void)layoutSubviews{

[super layoutSubviews];

//設置位置信息

CGFloat btnWidth = 74;

CGFloat btnHeight = 74;

CGFloat x = 0;

CGFloat y = 0;

int col = 0;//所在的列數

int row = 0;//所在的行數

int tolCol = 3;//總列數

CGFloat viewWidth = CGRectGetWidth(self.frame);

CGFloat viewHeight = CGRectGetHeight(self.frame);

CGFloat rowMargin = (viewWidth - tolCol*btnWidth)*0.25;//間距

CGFloat colMargin = (viewHeight - tolCol*btnHeight)*0.25;//間距




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

//繪制

UIButton *btn = self.subviews[i];

//計算所在的列數

col = i % tolCol;

row = i / tolCol;//所在行數

x = col*(rowMargin+btnWidth)+rowMargin;//x 與所在的列相關

y = row*(colMargin+btnHeight)+colMargin;

[btn setFrame:CGRectMake(x, y, btnWidth, btnHeight)];
}
}
@end

7、畫板

1》從相冊選擇照片
從相冊選擇照片 Expand source

//
// ViewController.m
// 20160421-畫板
//
// Created by devzkn on 4/21/16.
// Copyright © 2016 hisun. All rights reserved.
//

#import "ViewController.h"
#import "HSPaintView.h"
#import "HSHandleImageView.h"


@interface ViewController ()<UIImagePickerControllerDelegate,UINavigationControllerDelegate>
@property (weak, nonatomic) IBOutlet HSPaintView *paintView;

@end

@implementation ViewController
- (IBAction)lineWidthValueChange:(UISlider *)sender {
[self.paintView setLineWidth:sender.value];
}
- (IBAction)colorClick:(UIButton *)sender {
[self.paintView setColor:sender.backgroundColor];

}
#pragma mark - 清屏
- (IBAction)cleanScreen:(UIBarButtonItem *)sender {
[self.paintView cleanScreen];

}
- (IBAction)undo:(UIBarButtonItem *)sender {
[self.paintView undo];
}
- (IBAction)eraserClick:(UIBarButtonItem *)sender {
[self.paintView setColor:[UIColor whiteColor]];
}
#pragma mark - 從相冊選擇照片
- (IBAction)selectedPicture:(UIBarButtonItem*)sender {
//去用戶相冊
UIImagePickerController *pickerVc = [[UIImagePickerController alloc]init];
/*
UIImagePickerControllerSourceTypePhotoLibrary,相簿,默認值
UIImagePickerControllerSourceTypeCamera,//真機測試
UIImagePickerControllerSourceTypeSavedPhotosAlbum
*/

[pickerVc setSourceType:UIImagePickerControllerSourceTypeSavedPhotosAlbum];//照片
//modal
//設置代理
[pickerVc setDelegate:self];
[self presentViewController:pickerVc animated:YES completion:^{
//
}];

}
#pragma mark - UIImagePickerControllerDelegate
// The picker does not dismiss itself; the client dismisses it in these callbacks.
// The delegate will receive one or the other, but not both, depending whether the user
// confirms or cancels.
#if 0
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(nullable NSDictionary<NSString *,id> *)editingInfo{// NS_DEPRECATED_IOS(2_0, 3_0)
NSLog(@"%s",__func__);
}
#endif
/**
選中圖片的時候調用
*/

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
/**
{
UIImagePickerControllerMediaType = "public.image";
UIImagePickerControllerOriginalImage = "<UIImage: 0x7ae83b30> size {3000, 2002} orientation 0 scale 1.000000";
UIImagePickerControllerReferenceURL = "assets-library://asset/asset.JPG?id=ED7AC36B-A150-4C38-BB8C-B6D696F4F2ED&ext=JPG";
}
*/

UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
HSHandleImageView *handleView = [[HSHandleImageView alloc]initWithFrame:self.paintView.frame];
/*
回調,進行傳值,定義block的具體內容
*/

[handleView setBlock:^(UIImage *image){
[self.paintView setImage:image];
}];
[handleView setImage:image];//將選中的圖片顯示到handleView
[self.view addSubview:handleView];
[self dismissViewControllerAnimated:YES completion:^{
//
}];

}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{
NSLog(@"%s",__func__);
[self dismissViewControllerAnimated:YES completion:^{
//
}];
}

#pragma mark 保存圖片
- (IBAction)save:(UIBarButtonItem *)sender {
[self.paintView save];

}

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

@end

2》畫板的基本功能
畫板的輔助功能

//

// HSPaintView.m

// 20160421-畫板

//

// Created by devzkn on 4/21/16.

// Copyright © 2016 hisun. All rights reserved.

//




#import "HSPaintView.h"

#import "HSPaintPath.h"

#import "MBProgressHUD+MJ.h"




@interface HSPaintView ()

@property (nonatomic,strong) HSPaintPath *path;//存儲當前觸摸的路徑

@property (nonatomic,strong) NSMutableArray *paths;//存儲歷史的路徑







@end




@implementation HSPaintView




- (void)setImage:(UIImage *)image{

_image = image;

[self.paths addObject:image];

[self setNeedsDisplay];



}




- (void)cleanScreen{

[self.paths removeAllObjects];

[self setNeedsDisplay];



}

- (void)undo{

[self.paths removeLastObject];

[self setNeedsDisplay];



}




- (void)setLineWidth:(CGFloat)lineWidth{

_lineWidth = lineWidth;

}




- (void) setColor:(UIColor *)color{

_color = color;

}




- (NSMutableArray *)paths{

if (nil == _paths) {

_paths = [NSMutableArray array];

}

return _paths;

}




/** 獲取觸摸點*/

- (CGPoint) pointWithTouches:(NSSet *)touches{

UITouch *touch = [touches anyObject];

return [touch locationInView:self];

}

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

self.path = [HSPaintPath pathWithColor:self.color lineWidht:self.lineWidth startPoint:[self pointWithTouches:touches]];

[self.paths addObject:self.path];

}

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

//確定終點

[self.path addLineToPoint:[self pointWithTouches:touches]];

[self setNeedsDisplay];

}

#pragma mark - 繪制狀態

- (void)drawRect:(CGRect)rect{

if (!self.paths.count) {

return;

}

for (HSPaintPath *path in self.paths) {

if ([path isKindOfClass:[UIImage class]]) {

//畫圖片

UIImage *image = (UIImage *)path;

[image drawAtPoint:CGPointZero];

}else{

//設置繪制狀態

[path.color set];

//描邊

[path stroke];

}

}

}




#pragma mark -初始化繪制狀態

- (void)awakeFromNib{

self.lineWidth = 2;

}




#pragma mark - 保存到用戶相冊

- (void)save{

//1.截屏

UIGraphicsBeginImageContext(self.bounds.size);

//渲染

//獲取上下文

CGContextRef context = UIGraphicsGetCurrentContext();

[self.layer renderInContext:context];

//2.獲取新圖片

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

//3.關閉

UIGraphicsEndImageContext();

//4\保存

// NSData *data = UIImagePNGRepresentation(newImage);

// [data writeToFile:@"/Users/devzkn/Desktop/layer.png" atomically:YES];

//保存到用戶相冊里面

UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), context);

}




- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{

NSLog(@"%s",__func__);

if (error) {

//保存失敗

[MBProgressHUD showError:@"保存失敗"];




}else{

[MBProgressHUD showSuccess:@"保存成功"];

}

}

3》手勢識別器的處理
手勢識別器的使用

#if 1

- (instancetype)initWithFrame:(CGRect)frame{

self = [super initWithFrame:frame];

if (self) {

//設置自己的屬性

[self addUILongPressGestureRecognizer];

[self addUIRotationGestureRecognizer];

[self addUIPinchGestureRecognizer];



}

return self;

}

#endif




#pragma mark - 添加手勢識別器

- (void) addUILongPressGestureRecognizer{

UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];

[longPress setDelegate:self];

[self.imageView addGestureRecognizer:longPress];

}




- (void) addUIRotationGestureRecognizer{

UIRotationGestureRecognizer *rotationGR = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotationGR:)];

[rotationGR setDelegate:self];

[self.imageView addGestureRecognizer:rotationGR];

}




#pragma mark - rotation

- (void) rotationGR:(UIRotationGestureRecognizer*)rotationGR{

NSLog(@"%s",__func__);

[self.imageView setTransform:CGAffineTransformRotate(self.imageView.transform, rotationGR.rotation)];

[rotationGR setRotation:0];



}




- (void) addUIPinchGestureRecognizer{

UIPinchGestureRecognizer *pinchGR = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinchGR:)];

[pinchGR setDelegate:self];

[self.imageView addGestureRecognizer:pinchGR];}







- (void) pinchGR:(UIPinchGestureRecognizer*)pinchGR{

NSLog(@"%s",__func__);

[self.imageView setTransform:CGAffineTransformScale(self.imageView.transform, pinchGR.scale, pinchGR.scale)];

[pinchGR setScale:1];





}




#pragma mark - 長按處理

- (void) longPress:(UILongPressGestureRecognizer*)longPressGR{

if (longPressGR.state == UIGestureRecognizerStateEnded) {

//動畫

[UIView animateWithDuration:0.5 animations:^{

//設置透明度

[self.imageView setAlpha:0.3];

} completion:^(BOOL finished) {

//還原透明度

[UIView animateWithDuration:0.5 animations:^{

[self.imageView setAlpha:1.0];

}completion:^(BOOL finished) {

//1、截屏

UIGraphicsBeginImageContext(self.bounds.size);

[self.layer renderInContext:UIGraphicsGetCurrentContext()];

UIImage *newImage =UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

//2.將圖片傳遞給VC,進行渲染--使用block

self.block(newImage);

//3銷毀自己

[self removeFromSuperview];

}];

}];



}

}




- (void)setImage:(UIImage *)image{

_image = image;

[self.imageView setImage:image];

}

#pragma mark - gestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{

return YES;

}

總結

一、編程規范
1、枚舉類型變量通常以K開頭
枚舉定義 Expand source

/* Line cap styles. */

typedef CF_ENUM(int32_t, CGLineCap) {
kCGLineCapButt,

kCGLineCapRound,

kCGLineCapSquare
}
;

二、語法
1、字符串格式
1》轉義符號%

[self.textLable setText:[NSString stringWithFormat:@"%.2f%%",progressValue*100]];

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com