码迷,mamicode.com
首页 > 其他好文 > 详细

涂鸦板实现思路和步骤

时间:2015-09-14 19:24:18      阅读:203      评论:0      收藏:0      [点我收藏+]

标签:

------------- 基本思路 --------------

1. 搭建界面

 

2. 实现画线功能

2.1 实现画单笔

2.2 实现画多笔

2.3 实现设置线宽

2.4 实现设置线条颜色

 

3. 实现清屏

4. 实现回退

5. 实现橡皮擦

6. 实现保存到相册功能

7. 实现插入照片功能

 

------------- 基本思路 --------------

 

1. 使用自动布局搭界面

- 顶部的 UIToolBar

- 底部的自定义 View

- 中间的绘图用的 View

 

2. 实现基本的绘图功能

- 通过自定义 UIView 实现

1> 重写自定义 View 3个触摸方法

- touchBegan, 在这个方法中 moveToPoint

- touchMove, 在这个方法中 addLine

- drawRect, 在这个方法中把拼接好的路径渲染到 UIView上。

 

- 先实现绘制"单笔"

/** 参考代码:

 

 @interface SteveZPaintView ()

 @property (nonatomic, strong) UIBezierPath *path;

 @end

 

 

 @implementation SteveZPaintView

 

 

 - (CGPoint)pointWithTouches:(NSSet *)touches

 {

 UITouch *touch = touches.anyObject;

 return [touch locationInView:touch.view];

 }

 

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

 {

 self.path = [[UIBezierPath alloc] init];

 CGPoint point = [self pointWithTouches:touches];

 [self.path moveToPoint:point];

 }

 

 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

 {

 CGPoint point = [self pointWithTouches:touches];

 [self.path addLineToPoint:point];

 // 重绘

 [self setNeedsDisplay];

 }

 

 

 

 

 - (void)drawRect:(CGRect)rect {

 // Drawing code

 [self.path stroke];

 }

 

 */

 

- 实现画"多笔"

/** 参考代码:

 @interface SteveZPaintView ()

 //@property (nonatomic, strong) UIBezierPath *path;

 @property (nonatomic, strong) NSMutableArray *paths;

 @end

 

 

 @implementation SteveZPaintView

 

 

 - (CGPoint)pointWithTouches:(NSSet *)touches

 {

 UITouch *touch = touches.anyObject;

 return [touch locationInView:touch.view];

 }

 

 - (NSMutableArray *)paths

 {

 if (_paths == nil) {

 _paths = [NSMutableArray array];

 }

 return _paths;

 }

 

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

 {

 UIBezierPath *path = [[UIBezierPath alloc] init];

 CGPoint point = [self pointWithTouches:touches];

 [path moveToPoint:point];

 [self.paths addObject:path];

 }

 

 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

 {

 UIBezierPath *currentPath = [self.paths lastObject];

 

 CGPoint point = [self pointWithTouches:touches];

 [currentPath addLineToPoint:point];

 // 重绘

 [self setNeedsDisplay];

 }

 

 - (void)drawRect:(CGRect)rect {

 // Drawing code

 [self.path stroke];

 }

 

 

 */

 

 

- 实现设置"线宽"

1> slider 的值改变事件中, 把线宽传递给自定义 paintView

2> 需要在自定义 view 中添加一个 lineWidth的属性

3> 在绘图的时候设置 path 的线宽

/** 参考代码

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

 {

     UIBezierPath *path = [[UIBezierPath alloc] init];

     path.lineWidth = self.lineWidth;

     

     CGPoint point = [self pointWithTouches:touches];

     [path moveToPoint:point];

     [self.paths addObject:path];

 }

 

 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

 {

     UIBezierPath *currentPath = [self.paths lastObject];

     

     CGPoint point = [self pointWithTouches:touches];

     [currentPath addLineToPoint:point];

     // 重绘

     [self setNeedsDisplay];

 }

 

 - (void)drawRect:(CGRect)rect {

     // Drawing code

     for (UIBezierPath *path in self.paths) {

         path.lineCapStyle = kCGLineCapRound;

         path.lineJoinStyle = kCGLineJoinRound;

         [path stroke];

     }

 }

 

 */

 

- 实现设置"颜色"

* 因为每条线的颜色可能都不相同, 所以要为每条线保存一个颜色, 颜色要与 UIBezierPath对象保存在一起, 所以需要自定义一个类继承自 UIBezierPath, 添加一个颜色属性

* 然后在每次绘制的时候, 设置 path 对象的线条颜色

/** 参考:

 

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

 {

     SteveZBezierPath *path = [[SteveZBezierPath alloc] init];

     path.lineColor = self.lineColor;

     path.lineWidth = self.lineWidth;

     

     CGPoint point = [self pointWithTouches:touches];

     [path moveToPoint:point];

     [self.paths addObject:path];

 }

 

 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

 {

     SteveZBezierPath *currentPath = [self.paths lastObject];

     

     CGPoint point = [self pointWithTouches:touches];

     [currentPath addLineToPoint:point];

     // 重绘

     [self setNeedsDisplay];

 }

 

 - (void)drawRect:(CGRect)rect {

     // Drawing code

     for (SteveZBezierPath *path in self.paths) {

         path.lineCapStyle = kCGLineCapRound;

         path.lineJoinStyle = kCGLineJoinRound;

         [path.lineColor setStroke];

         [path stroke];

     }

 }

 

 

 */

 

 

- 清屏、回退、橡皮擦、保存

* 思路: 在控制器中调用在自定义 View中的下列方法

* 注意: 橡皮擦功能, 只需要设置笔的背景颜色与 绘图 view 的背景色一致。 然后绘图的时候就相当于"橡皮擦".

/** 参考:

 

 // 清屏

 - (void)clearScreen

 {

     [self.paths removeAllObjects];

     [self setNeedsDisplay];

 }

 

 // 回退(撤销)

 - (void)undo

 {

     [self.paths removeLastObject];

     [self setNeedsDisplay];

 }

 

 

 // 橡皮擦

 - (void)erase

 {

    self.lineColor = self.backgroundColor;

 // 此处不需要重绘, 点完橡皮擦以后,在 view move 的时候才需擦除

 }

 

 // 保存到相册

 - (void)save

 {

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

     CGContextRef ctx = UIGraphicsGetCurrentContext();

     [self.layer renderInContext:ctx];

     UIImage *imgPhoto = UIGraphicsGetImageFromCurrentImageContext();

     UIImageWriteToSavedPhotosAlbum(imgPhoto, nil, nil, nil);

 }

 

 

 */

 

 

 

 

- "选择照片"

* 思路:

0. "照片"按钮注册单击事件

1. 弹出选择照片的控制器

- 弹出 UIImagePickerController *pkVc = [[UIImagePickerController alloc] init];选择照片

- 设置 UIImagePickerController控制器的类型

pkVc.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;

- 设置控制器的代理(在代理方法中获取用户选择的图片, 并且把图片添加到"绘图 view ", 然后关闭被 modal 出来的控制器)

- 把选择照片的控制器 modal 出来

/** 参考:

 // 照片按钮的单击事件

 - (IBAction)pickPhoto:(id)sender {

 // 选择图片控制器

 UIImagePickerController *pkVc = [[UIImagePickerController alloc] init];

 

 

 // 设置照片的来源, 相册还是拍照等

 // UIImagePickerControllerSourceTypePhotoLibrary 按照相册(相簿)来选择

 // UIImagePickerControllerSourceTypeSavedPhotosAlbum 直接从照片库中选(按照时间)

 pkVc.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;

 

 

 // 设置代理(在代理方法中, 编写完成照片选择后要做的事情)

 pkVc.delegate = self;

 

 // 通过 modal 的方式展示控制器

 // modal 出来的控制器一般都是与主要操作不相关, 临时用一下

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

 }

 

 // UIImagePickerController的代理方法

 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info

 {

 // 查看字典中的内容

 //NSLog(@"%@", info);

 

 // 获取选择的图片

 UIImage *img = info[UIImagePickerControllerOriginalImage];

 

 // 创建图片框

 UIImageView *imgView = [[UIImageView alloc] initWithImage:img];

 

 // 把图片添加到绘图 viwe

 [self.paintView addSubview:imgView];

 

 // 关闭 modal 出的控制器

 [self dismissViewControllerAnimated:YES completion:nil];

 }

 

 */

 

 

 

 

* 补充一个知识点:

- 关于 modal 出的控制器如何关闭的问题

1> 大多数情况下在被 modal 出来的控制器内部关闭这个控制器是没有问题的

2> 只有在极少数情况下有问题。

3> 其实当在被 modal 出的控制器内部调用 dismiss方法的时候, 最终还是把这个 dismiss消息发送给了 modal出当前控制器的控制器

4> 最正确的情况是" Modal 出控制器, 谁负责 dismiss 控制器"

5> 但是如果" Modal 出控制器, 谁负责 dismiss 控制器", 那么势必很多情况下要使用代理, 所以为了简单, 大多数情况下都是"在被 Modal 出的子控制器内部执行 dismiss"

/*

 关于 dismiss modal 出来的控制器的讨论

 Discussion

 The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.

 */

 

 

 

 

 

 

2. 对添加到"绘图 View"中的 UIImageView控件实现"捏合""缩放""拖动""长按"各种效果

- 通过为 UIImageView添加各种"手势", 实现上面的功能

 

- 注意: "长按"UIImageView的时候, UIImageView中的图片集成到"绘图 View"

* 思路:

1> 直接把选中的图片 drawInRect: 绘制到"绘图 View"

2> 但是如果这个图片已经被缩放、旋转后, 再把图片直接绘制到"绘图 View"上就比较困难了

3> 解决: 先把 UIImageView添加到一个透明的 UIView , 然后再把这个透明的 UIView 内容绘制到一个 UIImage , 然后再把这个 UIImage绘制到"绘图上下文"中。

* 步骤:

1> 当选择好一个图片的时候, 创建一个自定 view(与"绘图 View"一样大), 然后把 UIImageView添加到这个自定义 View 中。

 

 

 

3. 为照片添加手势, 捏合、缩放、(拖动)平移、长按

* 注意: 默认图片框不支持与用户交互, 所以无法识别手势, 需要设置 userInteractionEnabled = YES;

/** 参考代码:

 

 - (instancetype)initWithFrame:(CGRect)frame photo:(UIImage *)image

 {

 if (self = [super initWithFrame:frame]) {

 

 UIImageView *imgViewPhoto = [[UIImageView alloc] initWithImage:image];

 imgViewPhoto.userInteractionEnabled = YES;

 [self addSubview:imgViewPhoto];

 self.imgViewPhoto = imgViewPhoto;

 

 // UIImageView 添加手势识别

 [self addGestures];

 

 }

 

 return self;

 }

 

 

 // 添加手势识别

 - (void)addGestures

 {

 // 1. 拖拽手势

 UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];

 [self.imgViewPhoto addGestureRecognizer:pan];

 

 

 // 2. 旋转手势

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

 rotation.delegate = self;

 [self.imgViewPhoto addGestureRecognizer:rotation];

 

 

 

 // 3. 捏合手势(缩放)

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

 pinch.delegate = self;

 [self.imgViewPhoto addGestureRecognizer:pinch];

 

 

 // 4. 长按手势(把图片绘制到 paint view )

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

 [self.imgViewPhoto addGestureRecognizer:longPress];

 }

 

 

 

 // 长按手势(把图片绘制到 paint view 中)

 - (void)longPressGesture:(UILongPressGestureRecognizer *)recognizer

 {

 if (recognizer.state == UIGestureRecognizerStateBegan) {

 

 // 1. 让图片闪一下, 在图片闪完以后把当前 view 中的内容渲染到 UIImage

 [UIView animateWithDuration:0.5 animations:^{

 

 self.imgViewPhoto.alpha = 0.5;

 

 } completion:^(BOOL finished) {

 

 [UIView animateWithDuration:0.5 animations:^{

 

 self.imgViewPhoto.alpha = 1.0;

 

 } completion:^(BOOL finished) {

 

 // 2. 通过调用代理, 把把图片绘制到 paint view

 // 把当前 view 渲染到 UIImage 当中, 然后通过代理把 UIIamge 传递回去

 if ([self.delegate respondsToSelector:@selector(photoView:withPhoto:)]) {

 UIImage *photoViewShot = [self imageWithViewShot];

 [self.delegate photoView:self withPhoto:photoViewShot];

 }

 

 }];

 }];

 }

 

 }

 

 // 把当前 view 渲染到 UIImage

 - (UIImage *)imageWithViewShot

 {

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

 CGContextRef ctx = UIGraphicsGetCurrentContext();

 [self.layer renderInContext:ctx];

 UIImage *img = UIGraphicsGetImageFromCurrentImageContext();

 UIGraphicsEndImageContext();

 return img;

 }

 

 

 // 捏合手势, 进行缩放

 - (void)pinchGesture:(UIPinchGestureRecognizer *)recognizer

 {

 recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);

 recognizer.scale = 1.0;

 

 }

 

 // 旋转手势

 - (void)rotationGesture:(UIRotationGestureRecognizer *)recognizer

 {

 CGFloat rotation =  recognizer.rotation;

 recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, rotation);

 recognizer.rotation = 0;

 }

 

 

 // 拖拽手势

 - (void)panGesture:(UIPanGestureRecognizer *)recognizer

 {

 CGPoint translation = [recognizer translationInView:recognizer.view];

 recognizer.view.transform = CGAffineTransformTranslate(recognizer.view.transform, translation.x, translation.y);

 [recognizer setTranslation:CGPointZero inView:recognizer.view];

 }

 

 */

 

 

4. 长按照片, 把照片集成到绘图视图中, 实现绘图

* 怎么把照片集成到绘图视图中?

- 直接把照片 drawInRect: 绘制到绘图视图中

- 但是如果照片被旋转、平移后如何把现在的照片绘制到视图中? 思路: 先把照片绘制到一个透明的图层(另外一个自定义 View)上。

 

* 把包含 UIImageView 的视图绘制到"绘图 view"上的具体步骤:

- 思考: "绘图 view"上的内容是如何绘制出来的

- 回答: 是在 Paint View drawInRect:方法中循环把一个一个的 UIBezierPath中的路径绘制上去的。

- 结论: 那么要想把图片绘制到 paint view , 就必须在 paths 集合中添加一个可以绘制图片的 path 对象, 然后在循环的时候, 就会把路径和图片一起绘制到 paint view上了。

- 步骤:

1> 在自定义 BezierPath 对象中添加一个 UIImage 属性, 用来保存要绘制的图片

2> paint view drawRect: 方法中判断如果当前的 path 对象有 image, 那么就绘制图片[path.image drawInRect:rect];否则就画线[path stroke];

3> 在自定义 paint view 中添加一个 image 属性, 用来接收从控制器中传递过来的要绘制到 paint view 上的图片

4> 重写 paint view setImage 方法, 在这个方法中, 创建一个自定义 BezierPath对象, 并且设置 image属性, 把这个 BezierPath对象添加到 self.paths 集合中, 然后执行重绘

 

/** 参考代码:

 ------------------- 自定义的 HMPhotoView-------------------

 #import <UIKit/UIKit.h>

 @class HMPhotoView;

 

 @protocol HMPhotoViewDelegate <NSObject>

 

 - (void)photoView:(HMPhotoView *)photoView withPhoto:(UIImage *)image;

 

 @end

 

 @interface HMPhotoView : UIView

 //@property (nonatomic, strong) UIImage *photo;

 

 - (instancetype)initWithFrame:(CGRect)frame photo:(UIImage *)image;

 

 @property (nonatomic, weak) id<HMPhotoViewDelegate> delegate;

 

 @end

 

 

 

 

 

 // 长按手势(把图片绘制到 paint view 中)

 - (void)longPressGesture:(UILongPressGestureRecognizer *)recognizer

 {

 if (recognizer.state == UIGestureRecognizerStateBegan) {

 // 1. 让图片闪一下, 在图片闪完以后把当前 view 中的内容渲染到 UIImage

 [UIView animateWithDuration:0.5 animations:^{

 self.imgViewPhoto.alpha = 0.5;

 } completion:^(BOOL finished) {

 [UIView animateWithDuration:0.5 animations:^{

 self.imgViewPhoto.alpha = 1.0;

 } completion:^(BOOL finished) {

 // 2. 通过调用代理, 把把图片绘制到 paint view

 // 把当前 view 渲染到 UIImage 当中, 然后通过代理把 UIIamge 传递回去

 if ([self.delegate respondsToSelector:@selector(photoView:withPhoto:)]) {

 UIImage *photoViewShot = [self imageWithViewShot];

 [self.delegate photoView:self withPhoto:photoViewShot];

 }

 

 }];

 }];

 }

 

 }

 ------------------- 自定义的 HMPhotoView-------------------

 

 

 

 

 

 

 

 

 

 

 

 

 

 ------------------- 自定义的 HMBezierPath-------------------

 #import <UIKit/UIKit.h>

 

 @interface HMBezierPath : UIBezierPath

 @property (nonatomic, strong) UIColor *lineColor;

 @property (nonatomic, strong) UIImage *image;  // 添加了一个 UIImage 属性

 @end

 ------------------- 自定义的 HMBezierPath-------------------

 

 

 

 

 

 

 

 

 

 

 ------------------- 自定义的绘图 HMPaintView-------------------

 1. 添加一个@property (nonatomic, strong) UIImage *image;属性

 2. 重写 setImage:方法

 - (void)setImage:(UIImage *)image

 {

 _image = image;

 HMBezierPath *path = [[HMBezierPath alloc] init];

 path.image = image;

 [self.paths addObject:path];

 

 // 重绘

 [self setNeedsDisplay];

 }

 

 3. 修改 drawRect:方法

 - (void)drawRect:(CGRect)rect {

 // paths数组中的每一个UIBezierPath对象都stroke一下

 for (HMBezierPath *path in self.paths) {

 

 if (path.image) {

 [path.image drawInRect:rect];

 } else {

 path.lineCapStyle = kCGLineCapRound;

 path.lineJoinStyle = kCGLineJoinRound;

 

 // 每个path对象在渲染之前, 设置使用自己的颜色来渲染

 [path.lineColor set];

 

 [path stroke];

 }

 }

 

 }

 

 ------------------- 自定义的绘图 HMPaintView-------------------

 

 

 

 

 

 

 

 -------------------控制器代码-------------------

 // photoView 的代理方法

 - (void)photoView:(HMPhotoView *)photoView withPhoto:(UIImage *)image

 {

 self.paintView.image = image;

 

 // 删除 HMPhotoView

 [self.imgViewPhoto removeFromSuperview];

 }

 

 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info

 {

 NSLog(@"%@", info);

 UIImage *photo = info[UIImagePickerControllerOriginalImage];

 HMPhotoView *imgViewPhoto = [[HMPhotoView alloc] initWithFrame:self.paintView.frame photo:photo];

 imgViewPhoto.delegate = self;

 [self.view addSubview:imgViewPhoto];

 self.imgViewPhoto = imgViewPhoto;

 

 // 关闭选择照片控制器

 [self dismissViewControllerAnimated:YES completion:nil];

 }

 

 -------------------控制器代码-------------------

 

 */

 

 

 

 

 

 

 

涂鸦板实现思路和步骤

标签:

原文地址:http://www.cnblogs.com/xhc1263478959/p/4807943.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!