Quartz2D實(shí)現(xiàn)簡單“畫圖板”

概念

1.首先要明白所有iOS中見到的所有東西,都是因?yàn)橥ㄟ^UIView內(nèi)部的圖層——CALayer對象;

并且,UIView顯示到屏幕上時(shí),會調(diào)用【 deawRect: 】方法進(jìn)行繪圖。

即:

》UIView負(fù)責(zé):交互、響應(yīng);

》CALayer負(fù)責(zé):展示、繪圖;

但——系統(tǒng)提供的可能無法滿足需求,那么我們就需要到Quartz2D(自定義UI控件)了。

2.圖形上下文CGContextRef三個(gè)作用:

》路徑

》狀態(tài)

》目標(biāo)

3.drawRect:方法

注意:

》與View相關(guān)的【圖形上下文】只能在 drawRect:方法中取得,但重寫 drawRect:方法需要一個(gè)UIView類,控制器類UIViewController中無法得到 drawRect:,往往需要自行創(chuàng)建一個(gè);

》drawRect不能手動(dòng)調(diào)用,有可能獲取不到正確的上下文;

》drawRect的調(diào)用時(shí)機(jī):

? ?①、在View第一次顯示時(shí);

? ?②、重繪:調(diào)用【 view的setNeedsDisplay 】或【setNeedsDisplayInRect:】的時(shí)候;



步驟:

一、搭建UI界面

二、畫筆功能:

1、重寫drawRect方法,需創(chuàng)建一個(gè)UIView類—— MayView,指定sb (storyboard) 中繪圖區(qū)View的class為 MayView;

2、在MayView類中創(chuàng)建一個(gè)可變數(shù)組用于存儲所有的路徑

//存儲路徑的數(shù)組:路徑不只有一條

@property (nonatomic, strong) NSMutableArray *allPaths;

并且進(jìn)行懶加載

#pragma mark - Getter & Setter

//懶加載

- (NSMutableArray *)allPaths {

if (_allPaths == nil) {

_allPaths = [NSMutableArray array];

}

return _allPaths;

}

3、思考:每一條線都有什么狀態(tài),怎么修改?想畫出一條線,怎么做?

a》注意:路徑的顏色的設(shè)置需要在 drawRect:方法中,并且因?yàn)槊恳粭l線都是獨(dú)立的,我們可能為每一條線設(shè)置不同的顏色等狀態(tài)。我們需要為每一個(gè)路徑,創(chuàng)建獨(dú)立的對象,讓它成為一個(gè)獨(dú)立的存在,并且擁有顏色屬性,那么我們可以通類對象來設(shè)置顏色屬性。因此,我們需要?jiǎng)?chuàng)建一個(gè)路徑類;

@interface MayPath : UIBezierPath

//每條路徑自己的顏色狀態(tài)

@property (nonatomic, strong) UIColor *pathColor;

@end

b》畫一條線:至少分兩步走:點(diǎn)擊 和 移動(dòng),所以需要調(diào)用touchesBegan方法和touchesMove方法

3-1.?調(diào)用方法

在MayView類中引入頭文件

#import "MayPath.h"

在“touchesBegan方法”里面創(chuàng)建一條路徑,并且添加到全局的路徑數(shù)組中(為什么每次都創(chuàng)建而不是用全局的?因?yàn)楫嫲宀豢赡苤稽c(diǎn)擊一次,所以我們要為每一次的點(diǎn)擊都創(chuàng)建一個(gè)路徑)

//開始點(diǎn)擊

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

//? ? 1.創(chuàng)建路徑

//每次點(diǎn)擊時(shí)候都創(chuàng)建一條MayPath對象路徑

MayPath * path = [MayPath bezierPath];

//? ? 將創(chuàng)建好的路徑添加到路徑數(shù)組中

[self.allPaths addObject:path];

3-2.這一步我們要在MayView.h文件中創(chuàng)建一個(gè)lineWidth屬性。(后面會將它與sb中的slider控件關(guān)聯(lián))

//線寬(設(shè)為公開屬性為外部調(diào)用,與slider控件關(guān)聯(lián))

@property (nonatomic, assign) CGFloat lineWidth;

回到.m文件,設(shè)置線寬,將定于好的lineWidth賦值過來

//? ? 2.設(shè)置線寬

[path setLineWidth:self.lineWidth];

3-3.我們需要回到控制器類ViewController中,引入自定義MayView類;

將MayView作為屬性傳入

#import "MayView.h"。

@interface ViewController ()

//拿到自定義view(畫板),以拿到畫板的屬性

@property (weak, nonatomic) IBOutlet MayView *paintView;

@end

然后,為sb中的slider控件拖一條方法連線,并且在屬性欄中更改它的值


//設(shè)置線寬與UISlider關(guān)聯(lián)

- (IBAction)setLineWidth:(UISlider *)sender {

self.paintView.lineWidth = sender.value;

}

這樣,lineWidth的值會隨著UISlider控件的變化而變化;

同時(shí)我們需要在viewDidLoad中設(shè)置線寬的初始值,不設(shè)置,一開始沒有點(diǎn)擊UISlider,那么默認(rèn)值為0,會無法顯示。

- (void)viewDidLoad {

[super viewDidLoad];

//設(shè)置線寬的初始值

self.paintView.lineWidth = 1;

}

3-4.回到MayView.m中

//? ? 連接處的樣式

[path setLineJoinStyle:kCGLineJoinRound];

//? ? 收尾樣式

[path setLineCapStyle:kCGLineCapRound];

3-5.設(shè)置顏色

像lineWidth屬性一樣,我們將lineColor設(shè)置為公開的屬性。然后設(shè)置路徑的顏色為lineColor

@property (nonatomic, strong) UIColor * lineColor;

//? ? 將顏色存儲在對象屬性中

path.pathColor = self.lineColor;

在控制器類中為顏色按鈕拖線,三個(gè)按鈕控件連接同一個(gè)方法。將按鈕的背景顏色傳給view的lineColor;

//設(shè)置顏色

- (IBAction)setLineColor:(UIButton *)sender {

self.paintView.lineColor = sender.backgroundColor;

}

回到MayView.m中

- (void)drawRect:(CGRect)rect {

//? ? 設(shè)置顏色

//? ? [[UIColor blackColor] setStroke];

//? ? 渲染(遍歷數(shù)組中所有路徑,進(jìn)行繪制)

for (MayPath * path in self.allPaths) {

//為路徑渲染相應(yīng)的顏色

[path.pathColor setStroke];

//渲染路徑

[path stroke];

}

}

3-6.開始描線

//? ? 3.設(shè)置起點(diǎn)

//? ? 3-1.獲取點(diǎn)擊對象

UITouch * touch =[touches anyObject];

//? ? 3-2.獲取點(diǎn)擊的位置

CGPoint currentBeganPoint = [touch locationInView:touch.view];

//? ? 4.設(shè)置起點(diǎn)

[path moveToPoint:currentBeganPoint];

//每次單擊都有效果

[path addLineToPoint:currentBeganPoint];

//重繪

[self setNeedsDisplay];

}

//點(diǎn)擊移動(dòng)- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent *)event {

//? ? 1.獲取點(diǎn)擊對象

UITouch * touch = [touches anyObject];

//? ? 2.獲取點(diǎn)擊位置

CGPoint? currentMovedPoint = [touch locationInView:touch.view];

//? ? 3.將點(diǎn)擊起點(diǎn)處 連線 到當(dāng)前點(diǎn)(取得起點(diǎn):就是數(shù)組中的最后一個(gè))

[[self.allPaths lastObject] addLineToPoint:currentMovedPoint];

//? ? 4.重繪/渲染? view

[self setNeedsDisplay];

}

三、清屏、撤銷、橡皮 功能

在MayView.h聲明方法,在.m中實(shí)現(xiàn),在控制器類中為三個(gè)按鈕拖線并且分別調(diào)用各自的方法

//清屏

- (void)clearScreen {

//將路徑數(shù)組清空

[self.allPaths removeAllObjects];

//? ? 重繪 view

[self setNeedsDisplay];

}

//撤銷

- (void)goBack {

//清除路徑最后一條

[self.allPaths removeLastObject];

//? ? 重繪 view

[self setNeedsDisplay];

}

//橡皮

- (void)erase{

self.lineColor = self.backgroundColor;

}

四、保存到相冊功能

4-1.在MayView.h聲明方法,在.m中實(shí)現(xiàn)

4-2.圖片類型的上下文:其實(shí)就是改目標(biāo)。開啟圖片類型的上下文:(默認(rèn)是view,但——只要你開啟了圖片類型的上下文,那么渲染的目標(biāo)就是內(nèi)部的UIImage對象

4-3.以后比較常用的開啟圖片上下文的方法:

UIGraphicsBeginImageContextWithOptions(大小,不透明,縮放)

大小:確定后里面圖片就是這個(gè)大小

縮放:設(shè)置為0就是根據(jù)你的屏幕設(shè)置它的點(diǎn)倍數(shù)

4-4.有開啟圖片上下文就要有關(guān)閉;UIGraphicsEndImageContext

4-5.小技巧

和繪圖有關(guān)的方法都是CGContext開頭

與上下文有關(guān)的方法是UIGraphics開頭

//保存

- (void)saveToPhoto {

//開啟圖片類型的上下文

UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);

CGContextRef context = UIGraphicsGetCurrentContext();

//圖層內(nèi)容給與到上下文

[self.layer renderInContext:context];

//獲取圖片

UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

//將圖片保存到相冊

UIImageWriteToSavedPhotosAlbum(img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

//? ? ? ? 關(guān)閉圖片類型上下文

UIGraphicsEndImageContext();

}

注意:保存到相冊的那個(gè)方法UIImageWriteToSavedPhotosAlbum:它內(nèi)部假如要調(diào)用一個(gè)方法只能是(image:didFinishSavingWithError:contextInfo)。官方文檔有說明;

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

if (error) {

NSLog(@"保存失敗");

}

else NSLog(@"保存成功");

}

在控制器類中為保存按鈕拖線并且調(diào)用方法

//保存

- (IBAction)beSave:(UIBarButtonItem *)sender {

//彈出提示框

UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"將保存到相冊" message:@"請輸入圖片的名字:" preferredStyle:UIAlertControllerStyleAlert];

[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {

}];

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

//? ? 添加按鈕

UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];

UIAlertAction * confirm = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

//? ? 文件保存名字

UITextField * tf = alert.textFields[0];

self.paintView.imgName = [NSString stringWithFormat:@"%@.png",tf.text];

[self.paintView saveToPhoto];

}];

[alert addAction:cancel];

[alert addAction:confirm];

}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Quartz2D以及drawRect的重繪機(jī)制字?jǐn)?shù)1487 閱讀21 評論1 喜歡1一、什么是Quartz2D Q...
    PurpleWind閱讀 930評論 0 3
  • Quartz2D 簡介及用途 Quartz 2D 是一個(gè)二維繪圖引擎,同時(shí)支持iOS和Mac系統(tǒng),Quartz2D...
    45b645c5912e閱讀 1,110評論 1 16
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,699評論 6 30
  • Quartz2D 簡介 Quartz2D是二維(平面)的繪圖引擎(經(jīng)包裝的函數(shù)庫,方便開發(fā)者使用。也就是說蘋果幫我...
    iOS_Cqlee閱讀 680評論 0 2
  • 轉(zhuǎn)載自cocoaChina http://www.cocoachina.com/bbs/read.php?tid...
    wzjmyff閱讀 505評論 0 0

友情鏈接更多精彩內(nèi)容