打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
iOS 下 实现手写签名(OC)

手写签字并保存图片
网上找了点现成的代码,又自己改了下...(前人方法)

首先新建一个PPSSignatureView继承GLKView

然后再使用的时候,创建一个继承GLKViewController的控制器,这里是关键,避免入坑,UIView addSubView的时候, glkView是无法显示的...

简单说,代码如下:

.h文件

  1. #import <UIKit/UIKit.h>  
  2. #import <GLKit/GLKit.h>  
  3.   
  4. @interface PPSSignatureView : GLKView  
  5. @property (nonatomic, strong) GLKBaseEffect *effect;  
  6. @property (nonatomic, assign) CGFloat colorR;  
  7. @property (nonatomic, assign) CGFloat colorG;  
  8. @property (nonatomic, assign) CGFloat colorB;  
  9. @property (nonatomic, assign) CGFloat alpha;  
  10. @property (nonatomic, assign) float maxFontSize;  
  11. @property (assign, nonatomic) UIColor *strokeColor;  
  12. @property (assign, nonatomic) BOOL hasSignature;  
  13. @property (strong, nonatomic) UIImage *signatureImage;  
  14. @property (nonatomic, strong) NSMutableArray *pointMArray;  
  15. /** 清除所有线条*/  
  16. - (void)erase;  
  17. @end  

.m文件

  1. #import "PPSSignatureView.h"  
  2. #import <OpenGLES/ES2/glext.h>  
  3.   
  4. #define             STROKE_WIDTH_MIN 0.004 // Stroke width determined by touch velocity  
  5. #define       STROKE_WIDTH_SMOOTHING 0.3   // Low pass filter alpha  
  6.   
  7. #define           VELOCITY_CLAMP_MIN 20  
  8. #define           VELOCITY_CLAMP_MAX 5000  
  9.   
  10. #define QUADRATIC_DISTANCE_TOLERANCE 3.0   // Minimum distance to make a curve  
  11.   
  12. #define             MAXIMUM_VERTECES 100000  
  13.   
  14.   
  15. static GLKVector3 StrokeColor = { 0, 0, 0 };  
  16. static float clearColor[4] = { 1, 1, 1, 0 };  
  17.   
  18. // Vertex structure containing 3D point and color  
  19. struct PPSSignaturePoint  
  20. {  
  21.     GLKVector3      vertex;  
  22.     GLKVector3      color;  
  23. };  
  24. typedef struct PPSSignaturePoint PPSSignaturePoint;  
  25.   
  26.   
  27. // Maximum verteces in signature  
  28. static const int maxLength = MAXIMUM_VERTECES;  
  29.   
  30.   
  31. // Append vertex to array buffer  
  32. static inline void addVertex(uint *length, PPSSignaturePoint v) {  
  33.     if ((*length) >= maxLength) {  
  34.         return;  
  35.     }  
  36.       
  37.     GLvoid *data = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);  
  38.     memcpy(data + sizeof(PPSSignaturePoint) * (*length), &v, sizeof(PPSSignaturePoint));  
  39.     glUnmapBufferOES(GL_ARRAY_BUFFER);  
  40.       
  41.     (*length)++;  
  42. }  
  43.   
  44. static inline CGPoint QuadraticPointInCurve(CGPoint start, CGPoint end, CGPoint controlPoint, float percent) {  
  45.     double a = pow((1.0 - percent), 2.0);  
  46.     double b = 2.0 * percent * (1.0 - percent);  
  47.     double c = pow(percent, 2.0);  
  48.       
  49.     return (CGPoint) {  
  50.         a * start.x + b * controlPoint.x + c * end.x,  
  51.         a * start.y + b * controlPoint.y + c * end.y  
  52.     };  
  53. }  
  54.   
  55. static float generateRandom(float from, float to) { return random() % 10000 / 10000.0 * (to - from) + from; }  
  56. static float clamp(float min, float max, float value) { return fmaxf(min, fminf(max, value)); }  
  57.   
  58.   
  59. // Find perpendicular vector from two other vectors to compute triangle strip around line  
  60. static GLKVector3 perpendicular(PPSSignaturePoint p1, PPSSignaturePoint p2) {  
  61.     GLKVector3 ret;  
  62.     ret.x = p2.vertex.y - p1.vertex.y;  
  63.     ret.y = -11 * (p2.vertex.x - p1.vertex.x);  
  64.     ret.z = 0;  
  65.     return ret;  
  66. }  
  67.   
  68. static PPSSignaturePoint ViewPointToGL(CGPoint viewPoint, CGRect bounds, GLKVector3 color) {  
  69.   
  70.     return (PPSSignaturePoint) {  
  71.         {  
  72.             (viewPoint.x / bounds.size.width * 2.0 - 1),  
  73.             ((viewPoint.y / bounds.size.height) * 2.0 - 1) * -1,  
  74.             0  
  75.         },  
  76.         color  
  77.     };  
  78. }  
  79.   
  80.   
  81. @interface PPSSignatureView () {  
  82.     // OpenGL state  
  83.     EAGLContext *context;  
  84.       
  85.       
  86.     GLuint vertexArray;  
  87.     GLuint vertexBuffer;  
  88.     GLuint dotsArray;  
  89.     GLuint dotsBuffer;  
  90.       
  91.       
  92.     // Array of verteces, with current length  
  93.     PPSSignaturePoint SignatureVertexData[maxLength];  
  94.     uint length;  
  95.       
  96.     PPSSignaturePoint SignatureDotsData[maxLength];  
  97.     uint dotsLength;  
  98.       
  99.       
  100.     // Width of line at current and previous vertex  
  101.     float penThickness;  
  102.     float previousThickness;  
  103.       
  104.       
  105.     // Previous points for quadratic bezier computations  
  106.     CGPoint previousPoint;  
  107.     CGPoint previousMidPoint;  
  108.     PPSSignaturePoint previousVertex;  
  109.     PPSSignaturePoint currentVelocity;  
  110. }  
  111.   
  112. @property (nonatomic, strong) NSMutableArray *drawPointArray;  
  113.   
  114. @end  
  115.   
  116.   
  117. @implementation PPSSignatureView  
  118.   
  119. - (NSMutableArray *)pointMArray {  
  120.     if(nil == _pointMArray) {  
  121.         _pointMArray = [NSMutableArray array];  
  122.     }  
  123.     return _pointMArray;  
  124. }  
  125.   
  126. - (void)commonInit {  
  127.     context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];  
  128.       
  129.     if (context) {  
  130.         time(NULL);  
  131.           
  132.         self.backgroundColor = [UIColor whiteColor];  
  133.         self.opaque = NO;  
  134.           
  135.         self.context = context;  
  136.         self.drawableDepthFormat = GLKViewDrawableDepthFormat24;  
  137.         self.enableSetNeedsDisplay = YES;  
  138.           
  139.         // Turn on antialiasing  
  140.         self.drawableMultisample = GLKViewDrawableMultisample4X;  
  141.           
  142.         [self setupGL];  
  143.           
  144.         // Capture touches  
  145.         UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];  
  146.         pan.maximumNumberOfTouches = pan.minimumNumberOfTouches = 1;  
  147.         pan.cancelsTouchesInView = YES;  
  148.         [self addGestureRecognizer:pan];  
  149.           
  150.         // For dotting your i's  
  151.         UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];  
  152.         tap.cancelsTouchesInView = YES;  
  153.         [self addGestureRecognizer:tap];  
  154.    
  155. #warning 这个地方是长按删除的操作  
  156.         // Erase with long press  
  157.         UILongPressGestureRecognizer *longer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];  
  158.         longer.cancelsTouchesInView = YES;  
  159.         [self addGestureRecognizer:longer];  
  160.           
  161.     } else [NSException raise:@"NSOpenGLES2ContextException" format:@"Failed to create OpenGL ES2 context"];  
  162. }  
  163.   
  164.   
  165. - (id)initWithCoder:(NSCoder *)aDecoder  
  166. {  
  167.     if (self = [super initWithCoder:aDecoder]) [self commonInit];  
  168.     return self;  
  169. }  
  170.   
  171.   
  172. - (id)initWithFrame:(CGRect)frame context:(EAGLContext *)ctx  
  173. {  
  174.     if (self = [super initWithFrame:frame context:ctx]) [self commonInit];  
  175.     return self;  
  176. }  
  177.   
  178.   
  179. - (void)dealloc  
  180. {  
  181.     [self tearDownGL];  
  182.       
  183.     if ([EAGLContext currentContext] == context) {  
  184.         [EAGLContext setCurrentContext:nil];  
  185.     }  
  186.     context = nil;  
  187. }  
  188.   
  189.   
  190. - (void)drawRect:(CGRect)rect  
  191. {  
  192.     // 清空当前所有颜色缓存  
  193.     glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);  
  194.     glClear(GL_COLOR_BUFFER_BIT);  
  195.   
  196.     [self.effect prepareToDraw];  
  197.       
  198.     // Drawing of signature lines  
  199.     if (length > 2) {  
  200.         glBindVertexArrayOES(vertexArray);  
  201.         glDrawArrays(GL_TRIANGLE_STRIP, 0, length);  
  202.     }  
  203.   
  204.     if (dotsLength > 0) {  
  205.         glBindVertexArrayOES(dotsArray);  
  206.         glDrawArrays(GL_TRIANGLE_STRIP, 0, dotsLength);  
  207.     }  
  208. }  
  209.   
  210. #warning 清除的操作  
  211. - (void)erase {  
  212.     length = 0;  
  213.     dotsLength = 0;  
  214.     self.hasSignature = NO;  
  215.       
  216.     [self setNeedsDisplay];  
  217. }  
  218.   
  219.   
  220.   
  221. - (UIImage *)signatureImage  
  222. {  
  223.     if (!self.hasSignature)  
  224.         return nil;  
  225.   
  226. //    self.hidden = YES;  
  227. //  
  228. //    self.strokeColor = [UIColor whiteColor];  
  229. //    [self setNeedsDisplay];  
  230.     UIImage *screenshot = [self snapshot];  
  231.       
  232. //    self.strokeColor = nil;  
  233. //  
  234. //    self.hidden = NO;  
  235.     return screenshot;  
  236. }  
  237.   
  238.   
  239. #pragma mark - Gesture Recognizers  
  240.   
  241.   
  242. - (void)tap:(UITapGestureRecognizer *)t {  
  243.   
  244.     CGPoint l = [t locationInView:self];  
  245.       
  246.     if (t.state == UIGestureRecognizerStateRecognized) {  
  247.         glBindBuffer(GL_ARRAY_BUFFER, dotsBuffer);  
  248.           
  249.         PPSSignaturePoint touchPoint = ViewPointToGL(l, self.bounds, (GLKVector3){1, 1, 1});  
  250.         addVertex(&dotsLength, touchPoint);  
  251.           
  252.         PPSSignaturePoint centerPoint = touchPoint;  
  253.         centerPoint.color = StrokeColor;  
  254.         addVertex(&dotsLength, centerPoint);  
  255.   
  256.         static int segments = 20;  
  257.         GLKVector2 radius = (GLKVector2){  
  258.             clamp(0.00001, 0.02, penThickness * generateRandom(0.5, 1.5)),  
  259.             clamp(0.00001, 0.02, penThickness * generateRandom(0.5, 1.5))  
  260.         };  
  261.         GLKVector2 velocityRadius = radius;  
  262.         float angle = 0;  
  263.           
  264.         for (int i = 0; i <= segments; i++) {  
  265.               
  266.             PPSSignaturePoint p = centerPoint;  
  267.             p.vertex.x += velocityRadius.x * cosf(angle);  
  268.             p.vertex.y += velocityRadius.y * sinf(angle);  
  269.               
  270.             addVertex(&dotsLength, p);  
  271.             addVertex(&dotsLength, centerPoint);  
  272.               
  273.             angle += M_PI * 2.0 / segments;  
  274.         }  
  275.                  
  276.         addVertex(&dotsLength, touchPoint);  
  277.           
  278.         glBindBuffer(GL_ARRAY_BUFFER, 0);  
  279.     }  
  280.       
  281.     [self setNeedsDisplay];  
  282. }  
  283.   
  284.   
  285. - (void)longPress:(UILongPressGestureRecognizer *)lp {  
  286.     [self erase];  
  287. }  
  288.   
  289. - (void)pan:(UIPanGestureRecognizer *)p {  
  290.       
  291.     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);  
  292.       
  293.     CGPoint v = [p velocityInView:self];  
  294.     CGPoint l = [p locationInView:self];  
  295.     //[self.drawPointArray addObject:];  
  296.       
  297.     currentVelocity = ViewPointToGL(v, self.bounds, (GLKVector3){0,0,0});  
  298.     float distance = 0.;  
  299.     if (previousPoint.x > 0) {  
  300.         distance = sqrtf((l.x - previousPoint.x) * (l.x - previousPoint.x) + (l.y - previousPoint.y) * (l.y - previousPoint.y));  
  301.     }      
  302.   
  303.     float velocityMagnitude = sqrtf(v.x*v.x + v.y*v.y);  
  304.     float clampedVelocityMagnitude = clamp(VELOCITY_CLAMP_MIN, VELOCITY_CLAMP_MAX, velocityMagnitude);  
  305.     float normalizedVelocity = (clampedVelocityMagnitude - VELOCITY_CLAMP_MIN) / (VELOCITY_CLAMP_MAX - VELOCITY_CLAMP_MIN);  
  306.       
  307.     float lowPassFilterAlpha = STROKE_WIDTH_SMOOTHING;  
  308.     float newThickness = (self.maxFontSize - STROKE_WIDTH_MIN) * (1 - normalizedVelocity) + STROKE_WIDTH_MIN;  
  309.     penThickness = penThickness * lowPassFilterAlpha + newThickness * (1 - lowPassFilterAlpha);  
  310.       
  311.     if ([p state] == UIGestureRecognizerStateBegan) {  
  312.           
  313.         previousPoint = l;  
  314.         previousMidPoint = l;  
  315.           
  316.         PPSSignaturePoint startPoint = ViewPointToGL(l, self.bounds, (GLKVector3){1, 1, 1});  
  317.         previousVertex = startPoint;  
  318.         previousThickness = penThickness;  
  319.           
  320.         addVertex(&length, startPoint);  
  321.         addVertex(&length, previousVertex);  
  322.           
  323.         self.hasSignature = YES;  
  324.           
  325.     } else if ([p state] == UIGestureRecognizerStateChanged) {  
  326.           
  327.         CGPoint mid = CGPointMake((l.x + previousPoint.x) / 2.0, (l.y + previousPoint.y) / 2.0);  
  328.           
  329.         if (distance > QUADRATIC_DISTANCE_TOLERANCE) {  
  330.             // Plot quadratic bezier instead of line  
  331.             unsigned int i;  
  332.               
  333.             int segments = (int) distance / 1.5;  
  334.               
  335.             float startPenThickness = previousThickness;  
  336.             float endPenThickness = penThickness;  
  337.             previousThickness = penThickness;  
  338.               
  339.             for (i = 0; i < segments; i++)  
  340.             {  
  341.                 penThickness = startPenThickness + ((endPenThickness - startPenThickness) / segments) * i;  
  342.                   
  343.                 CGPoint quadPoint = QuadraticPointInCurve(previousMidPoint, mid, previousPoint, (float)i / (float)(segments));  
  344.                   
  345.                 PPSSignaturePoint v = ViewPointToGL(quadPoint, self.bounds, StrokeColor);  
  346.                 [self addTriangleStripPointsForPrevious:previousVertex next:v];  
  347.                   
  348.                 previousVertex = v;  
  349.             }  
  350.         } else if (distance > 1.0) {  
  351.               
  352.             PPSSignaturePoint v = ViewPointToGL(l, self.bounds, StrokeColor);  
  353.             [self addTriangleStripPointsForPrevious:previousVertex next:v];  
  354.               
  355.             previousVertex = v;              
  356.             previousThickness = penThickness;  
  357.         }  
  358.           
  359.         previousPoint = l;  
  360.         previousMidPoint = mid;  
  361.   
  362.     } else if (p.state == UIGestureRecognizerStateEnded | p.state == UIGestureRecognizerStateCancelled) {  
  363.           
  364.         PPSSignaturePoint v = ViewPointToGL(l, self.bounds, (GLKVector3){1, 1, 1});  
  365.         addVertex(&length, v);  
  366.           
  367.         previousVertex = v;  
  368.         addVertex(&length, previousVertex);  
  369.     }  
  370.       
  371.     [self setNeedsDisplay];  
  372. }  
  373.   
  374.   
  375. - (void)setStrokeColor:(UIColor *)strokeColor {  
  376.     _strokeColor = strokeColor;  
  377.     [self updateStrokeColor];  
  378. }  
  379.   
  380.   
  381. #pragma mark - Private  
  382.   
  383. - (void)updateStrokeColor {  
  384.   
  385.     self.effect.constantColor = GLKVector4Make(_colorR, _colorG, _colorB, _alpha);  
  386. }  
  387.   
  388.   
  389. - (void)setBackgroundColor:(UIColor *)backgroundColor {  
  390.     [super setBackgroundColor:backgroundColor];  
  391.       
  392.     CGFloat red, green, blue, alpha, white;  
  393.     if ([backgroundColor getRed:&red green:&green blue:&blue alpha:&alpha]) {  
  394.         clearColor[0] = red;  
  395.         clearColor[1] = green;  
  396.         clearColor[2] = blue;  
  397.     } else if ([backgroundColor getWhite:&white alpha:&alpha]) {  
  398.         clearColor[0] = white;  
  399.         clearColor[1] = white;  
  400.         clearColor[2] = white;  
  401.     }  
  402. }  
  403.   
  404. - (void)bindShaderAttributes {  
  405.     glEnableVertexAttribArray(GLKVertexAttribPosition);  
  406.     glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(PPSSignaturePoint), 0);  
  407. //    glEnableVertexAttribArray(GLKVertexAttribColor);  
  408. //    glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE,  6 * sizeof(GLfloat), (char *)12);  
  409. }  
  410.   
  411. - (void)setupGL  
  412. {  
  413.     [EAGLContext setCurrentContext:context];  
  414.       
  415.     self.effect = [[GLKBaseEffect alloc] init];  
  416.       
  417.     [self updateStrokeColor];  
  418.   
  419.       
  420.     glDisable(GL_DEPTH_TEST);  
  421.       
  422.     // Signature Lines  
  423.     glGenVertexArraysOES(1, &vertexArray);  
  424.     glBindVertexArrayOES(vertexArray);  
  425.       
  426.     glGenBuffers(1, &vertexBuffer);  
  427.     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);  
  428.     glBufferData(GL_ARRAY_BUFFER, sizeof(SignatureVertexData), SignatureVertexData, GL_DYNAMIC_DRAW);  
  429.     [self bindShaderAttributes];  
  430.       
  431.       
  432.     // Signature Dots  
  433.     glGenVertexArraysOES(1, &dotsArray);  
  434.     glBindVertexArrayOES(dotsArray);  
  435.       
  436.     glGenBuffers(1, &dotsBuffer);  
  437.     glBindBuffer(GL_ARRAY_BUFFER, dotsBuffer);  
  438.     glBufferData(GL_ARRAY_BUFFER, sizeof(SignatureDotsData), SignatureDotsData, GL_DYNAMIC_DRAW);  
  439.     [self bindShaderAttributes];  
  440.       
  441.       
  442.     glBindVertexArrayOES(0);  
  443.   
  444.   
  445.     // Perspective  
  446.     GLKMatrix4 ortho = GLKMatrix4MakeOrtho(-1, 1, -1, 1, 0.1f, 2.0f);  
  447.     self.effect.transform.projectionMatrix = ortho;  
  448.       
  449.     GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -1.0f);  
  450.     self.effect.transform.modelviewMatrix = modelViewMatrix;  
  451.       
  452.     length = 0;  
  453.     penThickness = 0.003;  
  454.     previousPoint = CGPointMake(-100, -100);  
  455. }  
  456.   
  457.   
  458.   
  459. - (void)addTriangleStripPointsForPrevious:(PPSSignaturePoint)previous next:(PPSSignaturePoint)next {  
  460.     float toTravel = penThickness / 2.0;  
  461.       
  462.     for (int i = 0; i < 2; i++) {  
  463.         GLKVector3 p = perpendicular(previous, next);  
  464.         GLKVector3 p1 = next.vertex;  
  465.         GLKVector3 ref = GLKVector3Add(p1, p);  
  466.           
  467.         float distance = GLKVector3Distance(p1, ref);  
  468.         float difX = p1.x - ref.x;  
  469.         float difY = p1.y - ref.y;  
  470.         float ratio = -1.0 * (toTravel / distance);  
  471.           
  472.         difX = difX * ratio;  
  473.         difY = difY * ratio;  
  474.                   
  475.         PPSSignaturePoint stripPoint = {  
  476.             { p1.x + difX, p1.y + difY, 0.0 },  
  477.             StrokeColor  
  478.         };  
  479.         addVertex(&length, stripPoint);  
  480.           
  481.         toTravel *= -1;  
  482.     }  
  483. }  
  484.   
  485.   
  486. - (void)tearDownGL  
  487. {  
  488.     [EAGLContext setCurrentContext:context];  
  489.       
  490.     glDeleteVertexArraysOES(1, &vertexArray);  
  491.     glDeleteBuffers(1, &vertexBuffer);  
  492.       
  493.     glDeleteVertexArraysOES(1, &dotsArray);  
  494.     glDeleteBuffers(1, &dotsBuffer);  
  495.       
  496.     self.effect = nil;  
  497. }  
  498.   
  499. @end  





本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
ios中通过调试来使用私有api
UIPanGestureRecognizer学习笔记
【iOS系列】
三点画圆
UICollectionView 高级进阶篇
如何用PID算法,操控无人机悬停?
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服