Quartz2D实现简单“画图板”

概念

1.首先要明白所有iOS中见到的所有东西,都是因为通过UIView内部的图层——CALayer对象;

并且,UIView显示到屏幕上时,会调用【 deawRect: 】方法进行绘图。

即:

》UIView负责:交互、响应;

》CALayer负责:展示、绘图;

但——系统提供的可能无法满足需求,那么我们就需要到Quartz2D(自定义UI控件)了。

2.图形上下文CGContextRef三个作用:

》路径

》状态

》目标

3.drawRect:方法

注意:

》与View相关的【图形上下文】只能在 drawRect:方法中取得,但重写 drawRect:方法需要一个UIView类,控制器类UIViewController中无法得到 drawRect:,往往需要自行创建一个;

》drawRect不能手动调用,有可能获取不到正确的上下文;

》drawRect的调用时机:

   ①、在View第一次显示时;

   ②、重绘:调用【 view的setNeedsDisplay 】或【setNeedsDisplayInRect:】的时候;



步骤:

一、搭建UI界面

Quartz2D实现简单“画图板”_第1张图片

二、画笔功能:

1、重写drawRect方法,需创建一个UIView类—— MayView,指定sb (storyboard) 中绘图区View的class为 MayView;

2、在MayView类中创建一个可变数组用于存储所有的路径

//存储路径的数组:路径不只有一条

@property (nonatomic, strong) NSMutableArray *allPaths;

并且进行懒加载

#pragma mark - Getter & Setter

//懒加载

- (NSMutableArray *)allPaths {

if (_allPaths == nil) {

_allPaths = [NSMutableArray array];

}

return _allPaths;

}

3、思考:每一条线都有什么状态,怎么修改?想画出一条线,怎么做?

a》注意:路径的颜色的设置需要在 drawRect:方法中,并且因为每一条线都是独立的,我们可能为每一条线设置不同的颜色等状态。我们需要为每一个路径,创建独立的对象,让它成为一个独立的存在,并且拥有颜色属性,那么我们可以通类对象来设置颜色属性。因此,我们需要创建一个路径类;

@interface MayPath : UIBezierPath

//每条路径自己的颜色状态

@property (nonatomic, strong) UIColor *pathColor;

@end

b》画一条线:至少分两步走:点击 和 移动,所以需要调用touchesBegan方法和touchesMove方法

3-1. 调用方法

在MayView类中引入头文件

#import "MayPath.h"

在“touchesBegan方法”里面创建一条路径,并且添加到全局的路径数组中(为什么每次都创建而不是用全局的?因为画板不可能只点击一次,所以我们要为每一次的点击都创建一个路径)

//开始点击

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

//    1.创建路径

//每次点击时候都创建一条MayPath对象路径

MayPath * path = [MayPath bezierPath];

//    将创建好的路径添加到路径数组中

[self.allPaths addObject:path];

3-2.这一步我们要在MayView.h文件中创建一个lineWidth属性。(后面会将它与sb中的slider控件关联)

//线宽(设为公开属性为外部调用,与slider控件关联)

@property (nonatomic, assign) CGFloat lineWidth;

回到.m文件,设置线宽,将定于好的lineWidth赋值过来

//    2.设置线宽

[path setLineWidth:self.lineWidth];

3-3.我们需要回到控制器类ViewController中,引入自定义MayView类;

将MayView作为属性传入

#import "MayView.h"。

@interface ViewController ()

//拿到自定义view(画板),以拿到画板的属性

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

@end

然后,为sb中的slider控件拖一条方法连线,并且在属性栏中更改它的值


Quartz2D实现简单“画图板”_第2张图片

//设置线宽与UISlider关联

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

self.paintView.lineWidth = sender.value;

}

这样,lineWidth的值会随着UISlider控件的变化而变化;

同时我们需要在viewDidLoad中设置线宽的初始值,不设置,一开始没有点击UISlider,那么默认值为0,会无法显示。

- (void)viewDidLoad {

[super viewDidLoad];

//设置线宽的初始值

self.paintView.lineWidth = 1;

}

3-4.回到MayView.m中

//    连接处的样式

[path setLineJoinStyle:kCGLineJoinRound];

//    收尾样式

[path setLineCapStyle:kCGLineCapRound];

3-5.设置颜色

像lineWidth属性一样,我们将lineColor设置为公开的属性。然后设置路径的颜色为lineColor

@property (nonatomic, strong) UIColor * lineColor;

//    将颜色存储在对象属性中

path.pathColor = self.lineColor;

在控制器类中为颜色按钮拖线,三个按钮控件连接同一个方法。将按钮的背景颜色传给view的lineColor;

//设置颜色

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

self.paintView.lineColor = sender.backgroundColor;

}

回到MayView.m中

- (void)drawRect:(CGRect)rect {

//    设置颜色

//    [[UIColor blackColor] setStroke];

//    渲染(遍历数组中所有路径,进行绘制)

for (MayPath * path in self.allPaths) {

//为路径渲染相应的颜色

[path.pathColor setStroke];

//渲染路径

[path stroke];

}

}

3-6.开始描线

//    3.设置起点

//    3-1.获取点击对象

UITouch * touch =[touches anyObject];

//    3-2.获取点击的位置

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

//    4.设置起点

[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.将点击起点处 连线 到当前点(取得起点:就是数组中的最后一个)

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

//    4.重绘/渲染  view

[self setNeedsDisplay];

}

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

在MayView.h声明方法,在.m中实现,在控制器类中为三个按钮拖线并且分别调用各自的方法

//清屏

- (void)clearScreen {

//将路径数组清空

[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中实现

4-2.图片类型的上下文:其实就是改目标。开启图片类型的上下文:(默认是view,但——只要你开启了图片类型的上下文,那么渲染的目标就是内部的UIImage对象

4-3.以后比较常用的开启图片上下文的方法:

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

大小:确定后里面图片就是这个大小

缩放:设置为0就是根据你的屏幕设置它的点倍数

4-4.有开启图片上下文就要有关闭;UIGraphicsEndImageContext

4-5.小技巧

和绘图有关的方法都是CGContext开头

与上下文有关的方法是UIGraphics开头

//保存

- (void)saveToPhoto {

//开启图片类型的上下文

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

CGContextRef context = UIGraphicsGetCurrentContext();

//图层内容给与到上下文

[self.layer renderInContext:context];

//获取图片

UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

//将图片保存到相册

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

//        关闭图片类型上下文

UIGraphicsEndImageContext();

}

注意:保存到相册的那个方法UIImageWriteToSavedPhotosAlbum:它内部假如要调用一个方法只能是(image:didFinishSavingWithError:contextInfo)。官方文档有说明;

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

if (error) {

NSLog(@"保存失败");

}

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

}

在控制器类中为保存按钮拖线并且调用方法

//保存

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

}


你可能感兴趣的:(Quartz2D实现简单“画图板”)