在上一篇文章中我介绍了 UIBezierPath类 介绍 ,下面这篇文章介绍一下如何通过这个类实现一个简单的随手画画板的简单程序demo,功能包括:划线(可以调整线条粗细,颜色),撤销笔画,回撤笔画,清除画布,橡皮擦。当然也可以扩展其他的功能。

一、首先看看实现划线部分的关键代码吧!

- (void) drawRect: (CGRect) rect
{//绘制图片int width = self.pickedImage.size.width;int height = self.pickedImage.size.height;CGRect rectForImage = CGRectMake(0,0, width, height);[self.pickedImage drawInRect:rectForImage];if (self.arrayStrokes){int arraynum = 0;// each iteration draw a stroke// line segments within a single stroke (path) has the same color and line widthfor (NSDictionary *dictStroke in self.arrayStrokes){NSArray *arrayPointsInstroke = [dictStroke objectForKey:@"points"];UIColor *color = [dictStroke objectForKey:@"color"];float size = [[dictStroke objectForKey:@"size"] floatValue];[color set];     // Sets the color of subsequent stroke and fill operations to the color that the receiver represents.// draw the stroke, line by line, with rounded jointsUIBezierPath* pathLines = [UIBezierPath bezierPath];CGPoint pointStart = CGPointFromString([arrayPointsInstroke objectAtIndex:0]);[pathLines moveToPoint:pointStart];for (int i = 0; i < (arrayPointsInstroke.count - 1); i++){CGPoint pointNext = CGPointFromString([arrayPointsInstroke objectAtIndex:i+1]);[pathLines addLineToPoint:pointNext];}pathLines.lineWidth = size;pathLines.lineJoinStyle = kCGLineJoinRound; //拐角的处理pathLines.lineCapStyle = kCGLineCapRound; //最后点的处理[pathLines stroke];arraynum++;//统计笔画数量}}
}
// Start new dictionary for each touch, with points and color
- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event
{NSMutableArray *arrayPointsInStroke = [NSMutableArray array]; //点数组,相当于一个笔画NSMutableDictionary *dictStroke = [NSMutableDictionary dictionary];[dictStroke setObject:arrayPointsInStroke forKey:@"points"];[dictStroke setObject:self.currentColor forKey:@"color"];[dictStroke setObject:[NSNumber numberWithFloat:self.currentSize] forKey:@"size"];CGPoint point = [[touches anyObject] locationInView:self];[arrayPointsInStroke addObject:NSStringFromCGPoint(point)];[self.arrayStrokes addObject:dictStroke];//添加的是一个字典:点数组,颜色,粗细
}// Add each point to points array
- (void) touchesMoved:(NSSet *) touches withEvent:(UIEvent *) event
{CGPoint point = [[touches anyObject] locationInView:self];CGPoint prevPoint = [[touches anyObject] previousLocationInView:self];NSMutableArray *arrayPointsInStroke = [[self.arrayStrokes lastObject] objectForKey:@"points"];[arrayPointsInStroke addObject:NSStringFromCGPoint(point)];CGRect rectToRedraw = CGRectMake(\((prevPoint.x>point.x)?point.x:prevPoint.x)-currentSize,\((prevPoint.y>point.y)?point.y:prevPoint.y)-currentSize,\fabs(point.x-prevPoint.x)+2*currentSize,\fabs(point.y-prevPoint.y)+2*currentSize\);//Marks the specified rectangle of the receiver as needing to be redrawn.//在指定的rect范围进行重绘[self setNeedsDisplayInRect:rectToRedraw];//   [self setNeedsDisplay];
}

这里简单的说明介绍吧!

1、使用一个数组 arrayStrokes(称之为笔画数组) 来记录整一幅画,这个数组中保存的是一个个的字典,而这些字典就是这幅画中的每一笔画(而且是有顺序的),字典中有三项内容:包括笔画的size,color还有一个数组arrayPointsInStroke,注意:这个数组保存的touch  begin和move过程中经过的点的坐标(这些点统统用直线连接起来,就可以形成一个笔画了。当然,这个数组中是保存了好多个点的!所以连接起来笔画还是很逼真的!)。

2、那么在绘制的时候,就要用到 arrayStrokes 这个关键的数组了,从里面拿出每一个字典(一个字典就是代表一个笔画),根据字典中笔画的size,color和笔画所经过的点坐标,那么让UIBezierPath这个类来完成笔画的绘制就很简单了。

这样应该可以理解吧!

二、笔画的撤销和回撤的实现

我们知道每一个笔画都是通过一个字典来保存的,那么我们在画线的过程中对笔画的撤销和回撤那也就很简单了吧!

我们可以使用另一个数组 arrayAbandonedStrokes (称之为废弃数组)来保存我们所撤销的笔画,而撤销,肯定是我们所有笔画中的最后一划,所以我们在arrayAbandonedStrokes 废弃数组保存 arrayStrokes 笔画数组中的最后一个元素,同时将 arrayStrokes 笔画数组中的最后一个元素删除。这样就可以实现笔画的撤销。

反之,就是实现回撤了。即将废弃数组中的最后一个元素添加到笔画数组中,同时将废弃数组中的最后一个元素删除。

实现的代码如下:

//撤销
-(IBAction) undo {if ([arrayStrokes count]>0) {NSMutableDictionary* dictAbandonedStroke = [arrayStrokes lastObject];[self.arrayAbandonedStrokes addObject:dictAbandonedStroke];[self.arrayStrokes removeLastObject];[self setNeedsDisplay];}
}//回撤
-(IBAction) redo {if ([arrayAbandonedStrokes count]>0) {NSMutableDictionary* dictReusedStroke = [arrayAbandonedStrokes lastObject];[self.arrayStrokes addObject:dictReusedStroke];[self.arrayAbandonedStrokes removeLastObject];[self setNeedsDisplay];}
}

三、清楚画布的功能实现

//清除画布
-(IBAction) clearCanvas {self.pickedImage = nil;[self.arrayStrokes removeAllObjects];[self.arrayAbandonedStrokes removeAllObjects];[self setNeedsDisplay];
}

四、笔画颜色的选择

这里的处理是用到一个弹出框,点击可以选择颜色。

下面讲一下如何实现这个颜色选择器。其中参考自:点击打开链接

实现原理:弹出框中显示的是一张图片,我们通过一个函数处理,获取到这个图片的所有像素点的透明度和RGB(每一个值占1Byte)数据(是一个数组),然后通过点击获取到点的坐标,就可以获取到这个像素点的透明度和RGB值了。

实现的有关代码如下:

- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {UITouch* touch = [touches anyObject];CGPoint point = [touch locationInView:self.imgView]; //where image was tappedlastColor = [self getPixelColorAtLocation:point];[pickedColorDelegate pickedColor:(UIColor*)lastColor];
}// Please refer to iOS Developer Library for more details regarding the following two methods
- (UIColor*) getPixelColorAtLocation:(CGPoint)point {UIColor* color = nil;CGImageRef inImage = self.imgView.image.CGImage;// Create off screen bitmap context to draw the image into. Format ARGB is 4 bytes for each pixel: Alpa, Red, Green, BlueCGContextRef contexRef = [self createARGBBitmapContext:inImage];if (contexRef == NULL) { return nil; /* error */ }size_t w = CGImageGetWidth(inImage);     // problem!size_t h = CGImageGetHeight(inImage);CGRect rect = {{0,0},{w,h}};// Draw the image to the bitmap context. Once we draw, the memory// allocated for the context for rendering will then contain the// raw image data in the specified color space.CGContextDrawImage(contexRef, rect, inImage);// Now we can get a pointer to the image data associated with the bitmap// context.unsigned char* data = CGBitmapContextGetData (contexRef);if (data != NULL) {//offset locates the pixel in the data from x,y.//4 for 4 bytes of data per pixel, w is width of one row of data.int offset = 4*((w*round(point.y))+round(point.x)); //这是一个二维数组,offset是确定数组下标int alpha =  data[offset];int red = data[offset+1];int green = data[offset+2];int blue = data[offset+3];NSLog(@"offset: %i colors: RGB A %i %i %i  %i",offset,red,green,blue,alpha);color = [UIColor colorWithRed:(red/255.0f) green:(green/255.0f) blue:(blue/255.0f) alpha:(alpha/255.0f)];}// When finished, release the contextCGContextRelease(contexRef);// Free image data memory for the contextif (data) { free(data); }return color;
}- (CGContextRef)  createARGBBitmapContext:(CGImageRef) inImage {CGContextRef    context = NULL;CGColorSpaceRef colorSpace;void *          bitmapData;int             bitmapByteCount;int             bitmapBytesPerRow;// Get image width, height. We'll use the entire image.size_t pixelsWide = CGImageGetWidth(inImage);size_t pixelsHigh = CGImageGetHeight(inImage);// Declare the number of bytes per row. Each pixel in the bitmap in this// example is represented by 4 bytes; 8 bits each of red, green, blue, and// alpha.bitmapBytesPerRow   = (pixelsWide * 4);bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);// Use the generic RGB color space.//colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);  //deprecatedcolorSpace = CGColorSpaceCreateDeviceRGB();if (colorSpace == NULL){fprintf(stderr, "Error allocating color space\n");return NULL;}// Allocate memory for image data. This is the destination in memory// where any drawing to the bitmap context will be rendered.bitmapData = malloc( bitmapByteCount );if (bitmapData == NULL){fprintf (stderr, "Memory not allocated!");CGColorSpaceRelease( colorSpace );return NULL;}// Create the bitmap context. We want pre-multiplied ARGB, 8-bits// per component. Regardless of what the source image format is// (CMYK, Grayscale, and so on) it will be converted over to the format// specified here by CGBitmapContextCreate.context = CGBitmapContextCreate (bitmapData,pixelsWide,pixelsHigh,8,      // bits per componentbitmapBytesPerRow,colorSpace,kCGImageAlphaPremultipliedFirst);if (context == NULL){free (bitmapData);fprintf (stderr, "Context not created!");}// Make sure and release colorspace before returningCGColorSpaceRelease( colorSpace );return context;
}

iOS 使用UIBezierPath类实现随手画画板相关推荐

  1. iOS 使用UIBezierPath类实现随手画画板

    在上一篇文章中我介绍了 UIBezierPath类 介绍 ,下面这篇文章介绍一下如何通过这个类实现一个简单的随手画画板的简单程序demo,功能包括:划线(可以调整线条粗细,颜色),撤销笔画,回撤笔画, ...

  2. iOS_24_画画板(含取色板)

    终于效果例如以下: 一.简单说明1.使用一个数组 strokesArr(笔画数组)记录全部笔画.数组中保存的是一个个的笔画字典,一个字典就是一个笔画.笔画字典中有三项:笔画的大小.颜色.pointsA ...

  3. android实现简单的画画板

    画画板实现起来其实很简单,我们只需要利用android给我们提供的Canvas类来操作就可以实现简单的画画功能 直接看代码,注释都写清楚了 public class MainActivity exte ...

  4. ImageView实现画画板的功能

    1,使用虚拟机加载的图片是只读的,不能对其进行其他的操作.这在上一篇文章中已介绍,要想对其进行其他操作,还是同一个方法,就是创建其副本,对副本进行操作. 2,下面通过此知识点,再介绍一个通过Image ...

  5. 学习android 画板源代码,Android实现画画板案例

    郑州app开发画画板案例.布局代码是三个button和一个imagesview下面是图片. 布局代码就不展示了.下面是java代码. package cn.xhhkj.image; import an ...

  6. java线程画动图闪,Android中利用画图类和线程画出闪烁的心形,android心形,package com....

    Android中利用画图类和线程画出闪烁的心形,android心形,package com.package com.tt.view;import android.content.Context;imp ...

  7. Android 实现图片画画板

    本文主要讲述了Android 实现图片画画板 设计项目布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk ...

  8. 电脑PHP动画制作画板,涂鸦板简单实现 Html5编写属于自己的画画板

    这篇文章主要教大家如何使用Html5编写属于自己的画画板,进行绘画.调整颜色等操作,感兴趣的小伙伴们可以参考一下 最近了解到html5强大的绘图功能让我惊奇,于是,写了个小玩意---涂鸦板,能实现功能 ...

  9. 设计模式(二)——UML类图怎么画

    UML类图怎么画 一.注释(comment) 注释就是对类图的补充说明,可以附加在任何元素上,用右上角有折角的矩形来表示,其内放置说明文字,通过虚线连接被注释的元素. 二.类(Class) 在面向对象 ...

最新文章

  1. oracle两张表 比较好,比较Oracle两张表的数据是否一样
  2. linux启动x不启动桌面,redhat开机不启动桌面登录程序
  3. linux0.11内核编译,编译Linux-0.11内核
  4. Mac环境下sublime实现列选择或编辑的方法
  5. 如何使用 TRACERT 解决 Windows 中的 TCP/IP 问题
  6. UltraEdit中OracleSQL的syntax文档
  7. 直播预告|中台基石腾讯云TStack的正确使用姿势
  8. java switch case怎么判断范围_【转】Java期末复习攻略!
  9. 前端程序员书桌上不可缺少的CSS书籍
  10. 粉丝提问:求问大神您会查exif吗?
  11. pjsua帮助手册(中文)
  12. 为什么长视频没有强算法推荐的产品
  13. 骨传导技术:帮你摆脱噪音的困扰
  14. 数据挖掘之随机事件与随机变量
  15. 如何写好一个UITableView
  16. 动态规划: 数字三角形
  17. 【Feign请求头丢失问题】no suitable HttpMessageConverter found for response type
  18. meteor使用简介
  19. UC刘兰奇极速版制动刷金币
  20. 赶紧收藏!41个Web UI工具包资源免费及付费下载

热门文章

  1. 更换钢丝绳,为何选择“国标锻造”钢丝绳夹(非铸件)
  2. 期末复习测试大题浅析(吐槽)
  3. c 当前程序的语言,c语言实现获取macos当前的系统语言
  4. [BZOJ1499][NOI2005][DP+优化]瑰丽华尔兹
  5. 《嵌入式 – GD32开发实战指南》第12章 ADC
  6. 黑客是如何入侵服务器的,常见的攻击手段有哪些
  7. 荣耀机试题 2021.04 ~05
  8. Flink 窗口函数(Window Functions)处理迟到数据
  9. 办公自动化:PDF文件合并器,用Python将多个PDF文件进行合并
  10. 部署Kubernetes kube-apiserver启动失败