博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS开发之画图板(贝塞尔曲线)
阅读量:4550 次
发布时间:2019-06-08

本文共 6116 字,大约阅读时间需要 20 分钟。

  贝塞尔曲线,听着挺牛气一词,不过下面我们在做画图板的时候就用到贝塞尔绘直线,没用到绘制曲线的功能。如果会点PS的小伙伴会对贝塞尔曲线有更直观的理解。这篇博文的重点不在于如何用使用贝塞尔曲线,而是利用贝塞尔划线的功能来封装一个画图板。

  画图板的截图如下,上面的白板就是我们的画图板,是自己封装好的一个UIView,下面会详细的介绍如何封装这个画图板,下面的控件用来控制我们画图板的属性以及Undo,Redo和保存功能。点击保存时会把绘制的图片保存到手机的相册中。下面是具体的实现方案。

 

  一.封装画图板

    其实上面的白板就是一继承于UiView的一个子类,我们可以在这个子类中添加我们画图板相应的属性和方法,然后实例化成对象添加到ViewController中,当然为了省事添加白板的时候是通过storyboard来完成的,读者也可以自己实例化然后手动的添加到相应的ViewController中。

    1.封装白板的第一步是新建一个UIView的子类MyView,然后添加相应的属性和方法。MyView.h中的代码如下,代码具体意思请参照注释

1 #import 
2 3 @interface MyView : UIView 4 //用来设置线条的颜色 5 @property (nonatomic, strong) UIColor *color; 6 //用来设置线条的宽度 7 @property (nonatomic, assign) CGFloat lineWidth; 8 //用来记录已有线条 9 @property (nonatomic, strong) NSMutableArray *allLine;10 11 //初始化相关参数12 -(void)initMyView;13 //unDo操作14 -(void)backImage;15 //reDo操作16 -(void)forwardImage;17 18 @end

 

    2、上面的代码是对外的接口,有些属性我们是写在MyView.m的延展中以实现私有的目的,MyView延展部分如下:

1 @interface MyView()2 //声明贝塞尔曲线3 @property(nonatomic, strong) UIBezierPath *bezier;4 //存储Undo出来的线条5 @property(nonatomic, strong) NSMutableArray *cancleArray;6 @end

 

    3.下面的代码就是实现部分的代码了,会根据不同功能给出相应的说明

      (1).初始化我们的白板,给线条指定默认颜色和宽度并且给相应的变量分配内存空间,初始化代码如下:

1 //进行一些初始化工作2 -(void)initMyView3 {4     self.color = [UIColor redColor];5     self.lineWidth = 1;6     self.allLine = [NSMutableArray arrayWithCapacity:50];7     self.cancleArray = [NSMutableArray arrayWithCapacity:50];8 }

  

      (2)Undo功能的封装,相当于两个栈,把显示的线条出栈,进入为不显示的线条栈中,每执行一次此操作显示线条栈中的元素会少一条而不显示线条栈中会多一条,大致就这个意思吧,代码如下:

1 //UnDo操作 2 -(void)backImage 3 { 4     if (self.allLine.count > 0) 5     { 6         int index = self.allLine.count - 1; 7          8         [self.cancleArray addObject:self.allLine[index]]; 9         10         [self.allLine removeObjectAtIndex:index];11         12         [self setNeedsDisplay13          ];14     }15 }

    

      (3)Redo操作和Undo操作相反,从未显示栈中取出元素放入显示的栈中,代码中的栈我们是用数组来表示的,代码如下:

//ReDo操作-(void)forwardImage{    if (self.cancleArray.count > 0)    {        int index = self.cancleArray.count - 1;                [self.allLine addObject:self.cancleArray[index]];            [self.cancleArray removeObjectAtIndex:index];                [self setNeedsDisplay];    }}

 

      (4)、当开始触摸时我们新建一个BezierPath,把触摸起点设置成BezierPath的起点,并把将要画出的线条以及线条对应的属性封装成字典添加到显示栈中,代码如下

1 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 2 { 3     //新建贝塞斯曲线 4     self.bezier = [UIBezierPath bezierPath]; 5      6     //获取触摸的点 7     UITouch *myTouche = [touches anyObject]; 8     CGPoint point = [myTouche locationInView:self]; 9     10     //把刚触摸的点设置为bezier的起点11     [self.bezier moveToPoint:point];12     13     //把每条线存入字典中14     NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithCapacity:3];15     [tempDic setObject:self.color forKey:@"color"];16     [tempDic setObject:[NSNumber numberWithFloat:self.lineWidth] forKey:@"lineWidth"];17     [tempDic setObject:self.bezier forKey:@"line"];18     19     //把线加入数组中20     [self.allLine addObject:tempDic];21 22 }

 

      (5)当移动也就是划线的时候把点存储到BezierPath中,代码如下

1 -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 2 { 3     UITouch *myTouche = [touches anyObject]; 4     CGPoint point = [myTouche locationInView:self]; 5      6     [self.bezier addLineToPoint:point]; 7      8     //重绘界面 9     [self setNeedsDisplay];10     11 }

 

      (6)画出线条

1 // Only override drawRect: if you perform custom drawing. 2 // An empty implementation adversely affects performance during animation. 3 - (void)drawRect:(CGRect)rect 4 { 5     //对之前的线的一个重绘过程 6     for (int i = 0; i < self.allLine.count; i ++) 7     { 8         NSDictionary *tempDic = self.allLine[i]; 9         UIColor *color = tempDic[@"color"];10         CGFloat width = [tempDic[@"lineWidth"] floatValue];11         UIBezierPath *path = tempDic[@"line"];12         13         [color setStroke];14         [path setLineWidth:width];15         [path stroke];16     }17 18 }

 

  二.画图板的使用

    上面是封装画图板要用到的全部代码,下面的代码就是如何在ViewController中使用我们的画图板了,如何实例化控件,以及控件的初始化,注册回调等在这就不做赘述了,下面给出了主要控件的回调方法

    1、通过Slider来调节线条的宽度

1 //通过slider来设置线条的宽度2 - (IBAction)sliderChange:(id)sender3 {4     self.myView.lineWidth = self.mySlider.value;5 }

  

    2、通过SegmentControl来设置线条的颜色

1 /通过segmentControl来设置线条的颜色 2 - (IBAction)tapSegment:(id)sender { 3      4     switch (self.mySegment.selectedSegmentIndex) { 5         case 0: 6             self.myView.color = [UIColor redColor]; 7             break; 8         case 1: 9             self.myView.color = [UIColor blackColor];10             break;11         case 2:12             self.myView.color = [UIColor greenColor];13             break;14             15         default:16             break;17     }18     19 }

 

    3、undo和redo操作

1 //Undo 2 - (IBAction)tapBack:(id)sender { 3     [self.myView backImage]; 4 } 5  6  7 //Redo操作 8 - (IBAction)tapGo:(id)sender { 9     [self.myView forwardImage];10 }

 

    4.保存操作,也许下面的保存操作在处理方式上略显笨拙,如有更好的解决方案请留言。 保存的时候我是先截了个屏,然后把白板进行切割,把切割后图片存入到相册中,代码如下:

1 //把画的图保存到相册 2 - (IBAction)tapSave:(id)sender { 3     //截屏 4     UIGraphicsBeginImageContext(self.view.bounds.size); 5     [self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; 6     UIImage *uiImage = UIGraphicsGetImageFromCurrentImageContext(); 7     UIGraphicsEndImageContext(); 8      9     10     //截取画图版部分11     CGImageRef sourceImageRef = [uiImage CGImage];12     CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, CGRectMake(36, 6, 249, 352));13     UIImage *newImage = [UIImage imageWithCGImage:newImageRef];14     15     //把截的屏保存到相册16     UIImageWriteToSavedPhotosAlbum(newImage , nil, nil, nil);17     18     //给个保存成功的反馈19     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"存储照片成功"20                                                     message:@"您已将照片存储于图片库中,打开照片程序即可查看。"21                                                    delegate:self22                                           cancelButtonTitle:@"OK"23                                           otherButtonTitles:nil];24     [alert show];25 26 }

 

  以上就是本画图板的主要代码了,有不足之处还望批评指正。转载请注明出处。在本文结束时在来几张截图吧(demo下载地址:http://www.pgyer.com/LTQ8):

 

转载于:https://www.cnblogs.com/ludashi/p/3966559.html

你可能感兴趣的文章
决胜Flutter 第一章 熟悉战场
查看>>
如何开发优质的 Flutter App:Flutter App 软件调试指南
查看>>
决胜经典算法之冒泡排序
查看>>
决胜经典算法之选择排序
查看>>
11、求二进制中1的个数
查看>>
【nodejs】让nodejs像后端mvc框架(asp.net mvc)一样处理请求--请求处理结果适配篇(7/8)...
查看>>
CodeForces 731A Night at the Museum
查看>>
MySQL 删除数据库
查看>>
JavaScript 字符串(String) 对象
查看>>
How to use VisualSVN Server and TortoiseSVN to host your codes and control your codes' version
查看>>
微信小程序picker组件 - 省市二级联动
查看>>
Dynamics CRM 给视图配置安全角色
查看>>
Eclipse修改已存在的SVN地址
查看>>
C++ ACM基础
查看>>
(转)使用 python Matplotlib 库绘图
查看>>
进程/线程切换原则
查看>>
正则表达式语法
查看>>
20165301 2017-2018-2 《Java程序设计》第四周学习总结
查看>>
Vue的简单入门
查看>>
urllib 中的异常处理
查看>>