UI高级 一一 画板
来源:互联网 发布:淘宝药店可靠吗 编辑:程序博客网 时间:2024/04/30 05:05
本文源代码请参考: https://github.com/coderZYGui/drawingBoard
利用Quartz2D 实现画板功能,效果如下图
功能实现步骤:
1.界面搭建(使用autolayout自动布局),顶部使用Toorbar来管理多个item
中间画板是一个View,底部一个View上放一个UISlider和三个Button,并做布局操作.
2. 实现画线: 每条线都是新的path,所以把每个path都保存在数组中,画线的时候从数组中取出.
3. 设置属性功能: 设置清屏,撤销,UISlider等相关业务逻辑.因为清屏,撤销,橡皮擦,线宽,线色属于画板的功能
因此写在画板类中.
4. 绘制图片到画板: 从系统相册中选择图片后,对图片做拖动,旋转,平移等形变操作. 长按图片时,将图片绘制到画板中.
注意: 这里采用的方式是: 对一个UIView进行截屏操作,在UIView中放置一个UIImageView,将相册的image保存到UIImageView的image中.然后利用UIView的代理属性将图片传给画板的image.
代码如下:
ViewController文件
//// ViewController.m// DrawingBoard//// Created by 朝阳 on 2017/10/14.// Copyright © 2017年 sunny. All rights reserved.//#import "ViewController.h"#import "ZYDrawView.h"#import "ZYHandleImageView.h"@interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate,ZYHandleImageViewDelegate>@property (weak, nonatomic) IBOutlet ZYDrawView *drawView;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; }// 操作属于谁,写在谁的类中/** 清屏 */- (IBAction)clear:(UIBarButtonItem *)sender { [self.drawView clear];}/** 撤销 */- (IBAction)undo:(UIBarButtonItem *)sender { [self.drawView undo];}/** 橡皮擦 */- (IBAction)eraser:(UIBarButtonItem *)sender { [self.drawView eraser];}/** 设置线宽 */- (IBAction)setLineWidth:(UISlider *)slider { [self.drawView setLineWidth:slider.value];}/** 设置线颜色 */- (IBAction)setLineColor:(UIButton *)button { [self.drawView setLineColor:button.backgroundColor];}/** 照片 */- (IBAction)photo:(UIBarButtonItem *)sender { //从系统相册中选择一张图片 //1. 弹出系统相册 UIImagePickerController *pickerVC = [[UIImagePickerController alloc] init]; //2. 弹出相册的类型 pickerVC.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; //3. 设置pickerVC代理 pickerVC.delegate = self; //3. modal出系统相册 [self presentViewController:pickerVC animated:YES completion:nil];}#pragma -mark UIImagePickerControllerDelegate/** 当选中系统相册中的图片时会调用 @param picker 系统相册控制器 @param info 存放图片的字典 */- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{ NSLog(@"%@",info); // 系统相册是一个字典,根据key值来取出图片 UIImage *image = info[UIImagePickerControllerOriginalImage]; ZYHandleImageView *handleV = [[ZYHandleImageView alloc] initWithFrame:self.drawView.frame]; handleV.backgroundColor = [UIColor clearColor]; handleV.image = image; // 设置代理属性 handleV.delegate = self; [self.view addSubview:handleV]; // 系统相册modal消失 [self dismissViewControllerAnimated:YES completion:nil];}#pragma -mark ZYHandleImageViewDelegate- (void)handleImageView:(ZYHandleImageView *)handleImageView newImage:(UIImage *)newImage{ self.drawView.image = newImage;}/** 保存 */- (IBAction)save:(UIBarButtonItem *)sender { //1. 开启一个位图上下文 UIGraphicsBeginImageContextWithOptions(self.drawView.bounds.size, NO, 0); //2. 把drawView上的内容渲染到上下文中 CGContextRef ctx = UIGraphicsGetCurrentContext(); [self.drawView.layer renderInContext:ctx]; //3. 从上下文中生成新的图片 UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); //把图片保存到桌面// NSData *data = UIImagePNGRepresentation(newImage);// [data writeToFile:@"/Users/sunny/Desktop/photo.png" atomically:YES]; //4. 把图片保存到系统相册中 // 注意: 弹出系统相册必须实现 image:didFinishSavingWithError:contextInfo:方法 UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);}- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{ NSLog(@"saveSuccess");}/** 隐藏导航 */- (BOOL)prefersStatusBarHidden{ return YES;}@end
ZYDrawView文件(画板)
//// ZYDrawView.h// DrawingBoard//// Created by 朝阳 on 2017/10/14.// Copyright © 2017年 sunny. All rights reserved.//#import <UIKit/UIKit.h>@interface ZYDrawView : UIView@property (nonatomic, strong) UIImage *image;// 清屏- (void)clear;// 撤销- (void)undo;// 擦除- (void)eraser;// 设置线宽- (void)setLineWidth:(CGFloat)width;// 设置线的颜色- (void)setLineColor:(UIColor *)color;@end#import "ZYDrawView.h"#import "ZYBezierPath.h"/** 定义类扩展 */@interface ZYDrawView ()@property (nonatomic, strong) UIBezierPath *path;/** 用来保存所有的path */@property (nonatomic, strong) NSMutableArray *allPaths;/** 线宽 */@property (nonatomic, assign) CGFloat width;/** 线色 */@property (nonatomic, strong) UIColor *color;@end@implementation ZYDrawView- (NSMutableArray *)allPaths{ if (!_allPaths) { self.allPaths = [NSMutableArray array]; } return _allPaths;}- (void)awakeFromNib{ [super awakeFromNib]; // 添加手势 UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; [self addGestureRecognizer:pan]; // 设置一个默认线宽和线色 self.width = 1; self.color = [UIColor blackColor]; }// 清屏- (void)clear{ [self.allPaths removeAllObjects]; // 重绘 [self setNeedsDisplay];}// 撤销- (void)undo{ [self.allPaths removeLastObject]; [self setNeedsDisplay];}// 擦除- (void)eraser{ [self setLineColor:[UIColor whiteColor]];}// 设置线宽- (void)setLineWidth:(CGFloat)width{ self.width = width; }// 设置线的颜色- (void)setLineColor:(UIColor *)color{ self.color = color;}/** 拖动手势方法 */- (void)pan:(UIPanGestureRecognizer *)pan{ // 获取当前手指所在的点 CGPoint curP = [pan locationInView:self]; // 判断手势状态 if (pan.state == UIGestureRecognizerStateBegan) { // 创建路径 ZYBezierPath *path = [[ZYBezierPath alloc] init]; self.path = path; // 设置线宽 [path setLineWidth:self.width]; path.color = self.color; // 设置起点 [path moveToPoint:curP]; // 把路径保存到数组中 [self.allPaths addObject:self.path]; }else if(pan.state == UIGestureRecognizerStateChanged){ [self.path addLineToPoint:curP]; // 重绘,调用drawRect方法 [self setNeedsDisplay]; }}- (void)setImage:(UIImage *)image{ _image = image; // 把图片添加到数组中 [self.allPaths addObject:image]; // 重绘 [self setNeedsDisplay];}- (void)drawRect:(CGRect)rect { for (ZYBezierPath *path in self.allPaths) { // 判断path的真实类型 if ([path isKindOfClass:[UIImage class]]) { UIImage *image = (UIImage *)path; // 把图片绘制到画板中(填充整个区域) [image drawInRect:rect]; }else{ [path.color set]; [path stroke]; } } }@end
ZYBezierPath文件(继承自UIBezierPath,系统UIBezierPath功能不够用)
//// ZYBezierPath.h// DrawingBoard//// Created by 朝阳 on 2017/10/14.// Copyright © 2017年 sunny. All rights reserved.//#import <UIKit/UIKit.h>@interface ZYBezierPath : UIBezierPath@property (nonatomic,strong) UIColor *color;@end#import "ZYBezierPath.h"@implementation ZYBezierPath@end
ZYHandleImageView文件(分析中的UIView文件,继承UIView)
//// ZYHandleImageView.h// DrawingBoard//// Created by 朝阳 on 2017/10/14.// Copyright © 2017年 sunny. All rights reserved.//#import <UIKit/UIKit.h>@class ZYHandleImageView;@protocol ZYHandleImageViewDelegate <NSObject>- (void)handleImageView:(ZYHandleImageView *)handleImageView newImage:(UIImage *)newImage;@end@interface ZYHandleImageView : UIView@property (nonatomic, strong) UIImage *image;/** 代理属性 */@property (nonatomic, weak) id<ZYHandleImageViewDelegate> delegate;@end#import "ZYHandleImageView.h"/** 定义类扩展 */@interface ZYHandleImageView ()<UIGestureRecognizerDelegate>@property (nonatomic, strong) UIImageView *imageV;@end@implementation ZYHandleImageView- (UIImageView *)imageV{ if (!_imageV) { UIImageView *imageV = [[UIImageView alloc] initWithFrame:self.bounds]; imageV.userInteractionEnabled = YES; [self addSubview:imageV]; _imageV = imageV; // 添加手势 [self addGestures]; } return _imageV;}- (void)setImage:(UIImage *)image{ _image = image; self.imageV.image = image;}- (void)addGestures{ // 拖拽手势 UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; [self.imageV addGestureRecognizer:pan]; // 捏合 UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)]; pinch.delegate = self; [self.imageV addGestureRecognizer:pinch]; // 添加旋转 UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotation:)]; rotation.delegate = self; [self.imageV addGestureRecognizer:rotation]; // 长按 UILongPressGestureRecognizer *longP = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)]; [self.imageV addGestureRecognizer:longP];}// 拖动的时候调用- (void)pan:(UIPanGestureRecognizer *)pan{ // 手指移动后,相对于坐标中的偏移量 // 此时的pan.view 就相当于 UIImageView CGPoint transP = [pan translationInView:pan.view]; pan.view.transform = CGAffineTransformTranslate(pan.view.transform, transP.x, transP.y); // 复位 [pan setTranslation:CGPointZero inView:pan.view]; }// 捏合的时候调用- (void)pinch:(UIPinchGestureRecognizer *)pinch{ pinch.view.transform = CGAffineTransformScale(pinch.view.transform, pinch.scale, pinch.scale); //复位 [pinch setScale:1];}// 旋转的时候调用- (void)rotation:(UIRotationGestureRecognizer *)rotation{ rotation.view.transform = CGAffineTransformRotate(rotation.view.transform, rotation.rotation); //复位 [rotation setRotation:0];}// 长按的时候调用-(void)longPress:(UILongPressGestureRecognizer *)longPress{ // 长按时,图片闪烁一下 if (longPress.state == UIGestureRecognizerStateBegan) { [UIView animateWithDuration:0.3 animations:^{ self.imageV.alpha = 0; }completion:^(BOOL finished) { [UIView animateWithDuration:0.3 animations:^{ self.imageV.alpha = 1; }completion:^(BOOL finished) { // 把相册中的图片绘制到DrawView上 //1. 开启一个位图上下文 UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0); //2. 把imageV上的内容绘制到上下文中 CGContextRef ctx = UIGraphicsGetCurrentContext(); [self.layer renderInContext:ctx]; //3. 从上下文中生成新的图片 UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); //4. 关闭上下文 UIGraphicsEndImageContext(); // 若要把newImage绘制到DrawView上,需要使用代理进行传值 if ([self.delegate respondsToSelector:@selector(handleImageView:newImage:)]) { [self.delegate handleImageView:self newImage:newImage]; } //从父控件当中移除 [self removeFromSuperview]; }]; }]; }}//能够同时支持多个手势-(BOOL)gestureRecognizer:(nonnull UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(nonnull UIGestureRecognizer *)otherGestureRecognizer{ return YES;}@end
阅读全文
0 0
- UI高级 一一 画板
- Java小程序之高级画板UI篇
- UI 一一 UI基础知识
- UI 一一 数据刷新
- 画板UI实现
- 画板UI的实现
- UI 一一 基本控件认识
- UI 一一 常用控件-UIButton
- UI 一一 九宫格思想
- UI 一一 自定义批量删除
- UI 一一 UIApplication基本介绍
- Ui 一一 UIWindow基本介绍
- UI 画图下(画板画图)
- ui 画图三(画板画图)
- 黑马程序员一一高级开发工具Eclipse
- UI 一一 常用控件-UILabel,UIImageView
- UI 一一 UIScrollView的基本使用详解
- UI 一一 UIWebView的基本使用
- C语言几种输入 & 输出
- 射线检测与LayerMask
- 笔试_常见笔试题
- 游戏体验偶感
- 工作1年,写于秋中
- UI高级 一一 画板
- python中的矩阵运算
- java链表寻找中间节点
- java 中的向前引用
- 十道海量数据处理面试题
- 深度搜索 —— Max Area of Island
- 在idea中使用gradle,daemon自动退出的问题
- Java 8 获取某天最大(23:59:59)最小(00:00:00)时间
- Java代码覆盖工具