介绍:学了quartz2D的绘图知识后,我根据它的一些功能制作了一个小项目:涂鸦画板。
功能:绘制各种图形,还可以选取相册上的照片做涂鸦,然后保存到相册中。其中,还包括功能有:颜色的选取、线宽的选取、橡皮擦除、撤销上一次绘制痕迹、清除所有痕迹。
用到的自定义控件:工具栏控件(UIToolBar)、工具栏上的按钮控件(UIBarButtonItem)、警告框控件(UIAlertView、UIActionSheet)、图像选择器控制器控件(UIImagePickerController)等。
需要的类:除了应用程序代理类AppDelegate和控制器类ViewController外,还需要一个自定义的视图类DemoView和一个自定义的绘制路径类MyPath。MyPath类是用来封装绘制路径的,因为路径是结构体,所以不能直接动作对象使用,封装后可以将路径保存数组中,用来连续绘图。
具体操作如下:
1、创建如上介绍的类的截图为:
2、将自定义的视图类DemiView与控制器的视图关联起来:
3、下面就是具体针对每一个类的代码了:
<1>在MyPath.h文件中
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #define PHOTO_RECT (CGRectMake(0,0,375,667))//绘制照片局域宏定义 @interface MyPath : NSObject @property (assign,nonatomic)CGMutablePathRef path; //可变的路径 @property (strong,nonatomic)UIColor *color; //颜色 @property (assign,nonatomic)NSInteger lineWidth; //线宽 @property (strong,nonatomic)UIImage *image;//图像 @end
<2>在Application.m文件中:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //设置偏好 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setBool:YES forKey:@"isDefualtColor"]; [userDefaults setBool:YES forKey:@"isDefualtLine"]; [userDefaults synchronize]; return YES; }
<3>在DemoView类中
DemoView.h文件
#import <UIKit/UIKit.h> @interface DemoView : UIView @property (strong,nonatomic)UIColor *color; //颜色 @property (assign,nonatomic)NSInteger lineWidth; //线宽 @property (strong,nonatomic)NSMutableArray *paths; //可变数组,用来保存路径 @property(assign,nonatomic)BOOL isUndo; //标示符 @property (strong,nonatomic)UIImage *image; //图像 @property (assign,nonatomic)CGMutablePathRef path; //可变的路径 @end
DemoView.m文件
//懒加载
-(NSMutableArray*)paths { if (!_paths) { _paths = [NSMutableArray array]; } return _paths; }
//画图
- (void)drawRect:(CGRect)rect { //1.获取绘制图上下文 CGContextRef context = UIGraphicsGetCurrentContext(); //4.添加保存的路径到上下文 for (MyPath *myPath in self.paths) { CGContextAddPath(context, myPath.path); if(myPath.image) { [myPath.image drawInRect:PHOTO_RECT]; } //设置绘图属性 [myPath.color set]; CGContextSetLineWidth(context, myPath.lineWidth); //绘制路径 CGContextDrawPath(context, kCGPathStroke); } //如果是清除或撤销,就不执行当前绘图 if (!self.isUndo) { if(self.image) { [self.image drawInRect:PHOTO_RECT]; } //添加当前路径 CGContextAddPath(context, _path); //5.设置当前绘图属性 [self.color set]; CGContextSetLineWidth(context, self.lineWidth); //6.绘制路径 CGContextDrawPath(context, kCGPathStroke); } }
//开始触摸事件(设置绘制属性)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { self.isUndo = NO; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; if ([userDefaults boolForKey:@"isDefualtColor"]) { //设置默认颜色 self.color = [UIColor blackColor]; } if ([userDefaults boolForKey:@"isDefualtLine"]) { //设置默认线宽 self.lineWidth = 1.0; } //接收颜色通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(seclectColor:) name:@"ColorNitification" object:nil]; //接收线宽通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(selectedWidth:) name:@"WidthNitification" object:nil]; //创建路径 _path = CGPathCreateMutable(); //创建起始点 UITouch *touch = [touches anyObject]; CGPoint location = [touch locationInView:self]; CGPathMoveToPoint(_path, nil, location.x, location.y); }
//获取通知中的颜色和线宽数据
//从通知中获取颜色 -(void)seclectColor:(NSNotification*)notification { self.color = notification.object; if (self.color == NULL) { self.color = [UIColor blackColor]; } } //从通知中获取线宽 -(void)selectedWidth:(NSNotification*) notification { NSNumber *number = notification.object; self.lineWidth = [number integerValue]; if (self.lineWidth == 0) { self.lineWidth = 1.0; } }
//触摸移动(添加新路径,刷新视图)
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { if (_path) { //向路径添加新的直线 UITouch *touch = [touches anyObject]; CGPoint location = [touch locationInView:self]; CGPathAddLineToPoint(_path, nil, location.x, location.y); //让视图刷新 [self setNeedsDisplay]; } }
//触摸结束(封装路径并保存到数组中)
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { if (_path) { //保存路径 MyPath *myPath = [[MyPath alloc]init]; myPath.path = self.path; myPath.color = self.color; myPath.lineWidth = self.lineWidth; if(self.image) { myPath.image = self.image; self.image = nil; }
//保存路径 [self.paths addObject:myPath]; } }
//清理路径
-(void)dealloc { //清理保存的路径 for (MyPath *myPath in self.paths) { CGPathRelease(myPath.path); } }
<4>在ViewController.m文件中
//设置属性
@interface ViewController ()<UIAlertViewDelegate,UIActionSheetDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate> @property (strong,nonatomic)UIToolbar *toolBar; @property (strong,nonatomic)NSMutableDictionary *DicM; @end
//初始化和创建控件
- (void)viewDidLoad { [super viewDidLoad]; //初始化 self.DicM = [NSMutableDictionary dictionary]; ((DemoView *)self.view).isUndo = NO; //创建工具栏对象 self.toolBar = [[UIToolbar alloc]init]; self.toolBar.barTintColor = [UIColor brownColor]; self.toolBar.frame = CGRectMake(0, 0, 375, 40); //创建工具栏项目 UIBarButtonItem *colorItem = [[UIBarButtonItem alloc]initWithTitle:@"颜色" style:UIBarButtonItemStylePlain target:self action:@selector(SelectColor:)]; UIBarButtonItem *lineWidthItem = [[UIBarButtonItem alloc]initWithTitle:@"线宽" style:UIBarButtonItemStylePlain target:self action:@selector(SelectWidth:)]; UIBarButtonItem *clearpartItem = [[UIBarButtonItem alloc]initWithTitle:@"橡皮" style:UIBarButtonItemStylePlain target:self action:@selector(ClearPart:)]; UIBarButtonItem *backdoneItem = [[UIBarButtonItem alloc]initWithTitle:@"撤销" style:UIBarButtonItemStylePlain target:self action:@selector(BackDone:)]; UIBarButtonItem *clearallItem = [[UIBarButtonItem alloc]initWithTitle:@"清空" style:UIBarButtonItemStylePlain target:self action:@selector(ClearAll:)]; UIBarButtonItem *photoItem = [[UIBarButtonItem alloc]initWithTitle:@"照片" style:UIBarButtonItemStylePlain target:self action:@selector(selectPhoto:)]; UIBarButtonItem *saveItem = [[UIBarButtonItem alloc]initWithTitle:@"保存" style:UIBarButtonItemStylePlain target:self action:@selector(Save:)]; UIBarButtonItem *flexibleItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; [self.toolBar setItems:@[colorItem,flexibleItem,lineWidthItem,flexibleItem,clearpartItem,flexibleItem,backdoneItem,flexibleItem,clearallItem,flexibleItem,photoItem,flexibleItem,saveItem]]; [self.view addSubview:self.toolBar];
//开始时隐藏工具栏 self.toolBar.hidden = YES; //创建点击手势(双击时显示和隐藏工具栏) UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(showToolBar:)]; //设置点击次数 tap.numberOfTapsRequired = 2; //添加手势 [self.view addGestureRecognizer:tap]; }
//设置颜色选项
//选择颜色 -(void)SelectColor:(UIBarButtonItem *)sender { UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:nil message:@"可选颜色" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"红色",@"绿色",@"紫色",@"黄色",@"黑色",@"白色",@"蓝色",@"灰色",@"棕色",nil]; [self.DicM setObject:[UIColor redColor] forKey:@"红色"]; [self.DicM setObject:[UIColor greenColor] forKey:@"绿色"]; [self.DicM setObject:[UIColor purpleColor]forKey:@"紫色"]; [self.DicM setObject:[UIColor yellowColor]forKey:@"黄色"]; [self.DicM setObject:[UIColor blackColor] forKey:@"黑色"]; [self.DicM setObject:[UIColor whiteColor] forKey:@"白色"]; [self.DicM setObject:[UIColor blueColor] forKey:@"蓝色"]; [self.DicM setObject:[UIColor grayColor] forKey:@"灰色"]; [self.DicM setObject:[UIColor brownColor] forKey:@"棕色"]; [alertView show]; }
//设置线宽选项
//选择线宽 -(void)SelectWidth:(UIBarButtonItem *)sender { UIActionSheet *actionSheet = [[UIActionSheet alloc]initWithTitle:@"可选线宽" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"1号",@"2号",@"3号",@"4号",@"5号",@"6号",@"7号",@"8号",@"9号",@"10号",nil]; [self.DicM setObject:[NSNumber numberWithInteger:1] forKey:@"1号"]; [self.DicM setObject:[NSNumber numberWithInteger:2] forKey:@"2号"]; [self.DicM setObject:[NSNumber numberWithInteger:3] forKey:@"3号"]; [self.DicM setObject:[NSNumber numberWithInteger:4] forKey:@"4号"]; [self.DicM setObject:[NSNumber numberWithInteger:5] forKey:@"5号"]; [self.DicM setObject:[NSNumber numberWithInteger:6] forKey:@"6号"]; [self.DicM setObject:[NSNumber numberWithInteger:7] forKey:@"7号"]; [self.DicM setObject:[NSNumber numberWithInteger:8] forKey:@"8号"]; [self.DicM setObject:[NSNumber numberWithInteger:9] forKey:@"9号"]; [self.DicM setObject:[NSNumber numberWithInteger:10] forKey:@"10号"]; [actionSheet showInView:self.view]; }
//实现UIAlertView警告框协议
#pragma mark -<UIAlertViewDelegate> -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { NSString *key = [alertView buttonTitleAtIndex:buttonIndex]; UIColor *selectedcolor = [self.DicM objectForKey:key]; //发送通知附带颜色数据 [[NSNotificationCenter defaultCenter]postNotificationName:@"ColorNitification" object:selectedcolor]; //重设偏好 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"isDefualtColor"]; [[NSUserDefaults standardUserDefaults] synchronize]; }
//实现UIActionSheet警告框协议
#pragma mark -<UIActionSheetDelegate> -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { NSString *key = [actionSheet buttonTitleAtIndex:buttonIndex]; NSNumber *number = [self.DicM objectForKey:key]; //发送通知附带线宽数据 [[NSNotificationCenter defaultCenter]postNotificationName:@"WidthNitification" object:number]; //重设偏好 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"isDefualtLine"]; [[NSUserDefaults standardUserDefaults] synchronize]; }
//擦除功能
//橡皮擦除(其实就是用白色重绘) -(void)ClearPart:(UIBarButtonItem *)sender { UIColor *selectedcolor = [UIColor whiteColor]; [[NSNotificationCenter defaultCenter]postNotificationName:@"ColorNitification" object:selectedcolor]; NSNumber *number = [NSNumber numberWithInteger:10]; [[NSNotificationCenter defaultCenter]postNotificationName:@"WidthNitification" object:number]; //重设偏好 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"isDefualtColor"]; [[NSUserDefaults standardUserDefaults] synchronize]; [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"isDefualtLine"]; [[NSUserDefaults standardUserDefaults] synchronize]; }
//撤销功能
//撤销 -(void)BackDone:(UIBarButtonItem *)sender { //先做清理工作 MyPath *path = [((DemoView *)self.view).paths lastObject]; CGPathRelease(path.path); //删除最后一个路径 [((DemoView *)self.view).paths removeLastObject]; ((DemoView *)self.view).image = nil; ((DemoView *)self.view).isUndo = YES; //让视图重绘 [self.view setNeedsDisplay]; }
//清空功能
//清空绘图 -(void)ClearAll:(UIBarButtonItem *)sender { //先做清理工作 for(MyPath *path in ((DemoView *)self.view).paths) { CGPathRelease(path.path); } //删除所有 [((DemoView *)self.view).paths removeAllObjects]; ((DemoView *)self.view).image = nil; ((DemoView *)self.view).isUndo = YES; //让视图重绘 [self.view setNeedsDisplay]; }
//保存绘图功能
//保存绘图 -(void)Save:(UIBarButtonItem *)sender { [self didSelectedSave]; } -(void)didSelectedSave { //开始图像绘制上下文 UIGraphicsBeginImageContext(self.view.bounds.size); CGContextRef context = UIGraphicsGetCurrentContext(); // 先画保存的path for(MyPath *myPath in ((DemoView *)self.view).paths) { if(myPath.image) { [myPath.image drawInRect: PHOTO_RECT ]; } CGContextAddPath(context, myPath.path); [myPath.color set]; CGContextSetLineWidth(context, myPath.lineWidth); CGContextStrokePath(context); } //获取绘制的图片 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); //结束图像绘制上下文 UIGraphicsEndImageContext(); //保存图片 UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL); } - (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo { NSString *msg = nil; if(error) { msg = @"图片保存失败"; } else { msg = @"图片保存成功"; } UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"保存图片" message:msg delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; }
//选取照片功能
//选取照片 -(void)selectPhoto:(UIBarButtonItem*)sender { [self didSelectedPhoto]; } -(void)didSelectedPhoto { //显示照片选择窗口 UIImagePickerController *picker = [[UIImagePickerController alloc]init]; //图片来源是相册 picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; picker.delegate = self; //以模态窗口的形式显示图片 [self presentViewController:picker animated:YES completion:nil]; }
//图像选择器控制器协议方法
#pragma mark - imagePikerController 代理方法 //选取图片 -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { //取出图片 UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; ((DemoView *)self.view).image = image; ((DemoView *)self.view).isUndo = NO; //创建路径 ((DemoView *)self.view).path = CGPathCreateMutable(); //关闭模态窗口 [picker dismissViewControllerAnimated:YES completion:nil]; //刷新视图 [self.view setNeedsDisplay]; }
//手势事件,是否显示工具栏
//按钮事件是否显示工具栏 -(void)showToolBar:(UIButton *)sender { self.toolBar.hidden = !self.toolBar.hidden; }
演示截图如下:
开始时: 双击显示工具栏:
(使用默认颜色和线宽)绘制直线 选颜色绘制直线 ,选红色
接着选择线宽绘制直线:选10号
点击橡皮擦除: 点击撤销,被擦除部分还原
点击清空,什么都没有了: 点击照片,打开相册
任意选取一张照片,选取X
绘制图片并保存到相册:
查看保存的图片
可以再双击隐藏工具栏: