概念:
1.首先要明白所有iOS中見到的所有東西述雾,都是因為通過UIView內(nèi)部的圖層——CALayer對象;
并且溅漾,UIView顯示到屏幕上時哎甲,會調(diào)用【 deawRect: 】方法進行繪圖。
即:
》UIView負(fù)責(zé):交互啤挎、響應(yīng)驻谆;
》CALayer負(fù)責(zé):展示、繪圖庆聘;
但——系統(tǒng)提供的可能無法滿足需求旺韭,那么我們就需要到Quartz2D(自定義UI控件)了。
2.圖形上下文CGContextRef三個作用:
》路徑
》狀態(tài)
》目標(biāo)
3.drawRect:方法
注意:
》與View相關(guān)的【圖形上下文】只能在 drawRect:方法中取得掏觉,但重寫 drawRect:方法需要一個UIView類区端,控制器類UIViewController中無法得到 drawRect:,往往需要自行創(chuàng)建一個澳腹;
》drawRect不能手動調(diào)用织盼,有可能獲取不到正確的上下文;
》drawRect的調(diào)用時機:
? ?①酱塔、在View第一次顯示時沥邻;
? ?②、重繪:調(diào)用【 view的setNeedsDisplay 】或【setNeedsDisplayInRect:】的時候羊娃;
步驟:
一唐全、搭建UI界面
二、畫筆功能:
1蕊玷、重寫drawRect方法邮利,需創(chuàng)建一個UIView類—— MayView,指定sb (storyboard) 中繪圖區(qū)View的class為 MayView垃帅;
2延届、在MayView類中創(chuàng)建一個可變數(shù)組用于存儲所有的路徑
//存儲路徑的數(shù)組:路徑不只有一條
@property (nonatomic, strong) NSMutableArray *allPaths;
并且進行懶加載
#pragma mark - Getter & Setter
//懶加載
- (NSMutableArray *)allPaths {
if (_allPaths == nil) {
_allPaths = [NSMutableArray array];
}
return _allPaths;
}
3、思考:每一條線都有什么狀態(tài)贸诚,怎么修改方庭?想畫出一條線,怎么做酱固?
a》注意:路徑的顏色的設(shè)置需要在 drawRect:方法中械念,并且因為每一條線都是獨立的,我們可能為每一條線設(shè)置不同的顏色等狀態(tài)运悲。我們需要為每一個路徑龄减,創(chuàng)建獨立的對象,讓它成為一個獨立的存在扇苞,并且擁有顏色屬性欺殿,那么我們可以通類對象來設(shè)置顏色屬性寄纵。因此鳖敷,我們需要創(chuàng)建一個路徑類脖苏;
@interface MayPath : UIBezierPath
//每條路徑自己的顏色狀態(tài)
@property (nonatomic, strong) UIColor *pathColor;
@end
b》畫一條線:至少分兩步走:點擊 和 移動,所以需要調(diào)用touchesBegan方法和touchesMove方法
3-1.?調(diào)用方法
在MayView類中引入頭文件
#import "MayPath.h"
在“touchesBegan方法”里面創(chuàng)建一條路徑定踱,并且添加到全局的路徑數(shù)組中(為什么每次都創(chuàng)建而不是用全局的棍潘?因為畫板不可能只點擊一次,所以我們要為每一次的點擊都創(chuàng)建一個路徑)
//開始點擊
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {
//? ? 1.創(chuàng)建路徑
//每次點擊時候都創(chuàng)建一條MayPath對象路徑
MayPath * path = [MayPath bezierPath];
//? ? 將創(chuàng)建好的路徑添加到路徑數(shù)組中
[self.allPaths addObject:path];
3-2.這一步我們要在MayView.h文件中創(chuàng)建一個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控件的變化而變化呵恢;
同時我們需要在viewDidLoad中設(shè)置線寬的初始值,不設(shè)置媚创,一開始沒有點擊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;
在控制器類中為顏色按鈕拖線芒炼,三個按鈕控件連接同一個方法挥唠。將按鈕的背景顏色傳給view的lineColor;
//設(shè)置顏色
- (IBAction)setLineColor:(UIButton *)sender {
self.paintView.lineColor = sender.backgroundColor;
}
回到MayView.m中
- (void)drawRect:(CGRect)rect {
//? ? 設(shè)置顏色
//? ? [[UIColor blackColor] setStroke];
//? ? 渲染(遍歷數(shù)組中所有路徑焕议,進行繪制)
for (MayPath * path in self.allPaths) {
//為路徑渲染相應(yīng)的顏色
[path.pathColor setStroke];
//渲染路徑
[path stroke];
}
}
3-6.開始描線
//? ? 3.設(shè)置起點
//? ? 3-1.獲取點擊對象
UITouch * touch =[touches anyObject];
//? ? 3-2.獲取點擊的位置
CGPoint currentBeganPoint = [touch locationInView:touch.view];
//? ? 4.設(shè)置起點
[path moveToPoint:currentBeganPoint];
//每次單擊都有效果
[path addLineToPoint:currentBeganPoint];
//重繪
[self setNeedsDisplay];
}
//點擊移動- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent *)event {
//? ? 1.獲取點擊對象
UITouch * touch = [touches anyObject];
//? ? 2.獲取點擊位置
CGPoint? currentMovedPoint = [touch locationInView:touch.view];
//? ? 3.將點擊起點處 連線 到當(dāng)前點(取得起點:就是數(shù)組中的最后一個)
[[self.allPaths lastObject] addLineToPoint:currentMovedPoint];
//? ? 4.重繪/渲染? view
[self setNeedsDisplay];
}
三宝磨、清屏、撤銷盅安、橡皮 功能
在MayView.h聲明方法唤锉,在.m中實現(xiàn),在控制器類中為三個按鈕拖線并且分別調(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中實現(xiàn)
4-2.圖片類型的上下文:其實就是改目標(biāo)。開啟圖片類型的上下文:(默認(rèn)是view蝙寨,但——只要你開啟了圖片類型的上下文晒衩,那么渲染的目標(biāo)就是內(nèi)部的UIImage對象
4-3.以后比較常用的開啟圖片上下文的方法:
UIGraphicsBeginImageContextWithOptions(大小嗤瞎,不透明,縮放)
大小:確定后里面圖片就是這個大小
縮放:設(shè)置為0就是根據(jù)你的屏幕設(shè)置它的點倍數(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();
}
注意:保存到相冊的那個方法UIImageWriteToSavedPhotosAlbum:它內(nèi)部假如要調(diào)用一個方法只能是(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];
}