iOS开发:手势解锁(带路线相交检测)
2016-12-28 15:07
561 查看
一个普通的手势解锁插件,可以判断路线交叉
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/28/fcd5c781d7a2753be81940a5a6c3383d)
dot和line,用ios自带绘图来做
(2)密码处理
密码可以是字符串,存到本地,或者用于二次加密
(3)线段相交检测
如果要求画出的手势路线不能相交,需要线段相交算法,从线段获得两个端点的坐标
github:手势解锁
预览
思路
(1)画点画线dot和line,用ios自带绘图来做
#pragma mark - 搭建初始UI - (void)createUI { // 提示语 tipLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.frame.size.width / 2 - 100, 60, 200, 30)]; tipLabel.text = @"请输入手势"; tipLabel.textAlignment = NSTextAlignmentCenter; tipLabel.textColor = [UIColor redColor]; [self addSubview:tipLabel]; // 按钮 UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.frame = CGRectMake(self.frame.size.width / 2 - 50, 120, 100, 30); [button setTitle:@"隐藏手势" forState:UIControlStateNormal]; [button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; [button addTarget:self action:@selector(hide) forControlEvents:UIControlEventTouchUpInside]; [self addSubview:button]; // 九宫格点阵,每个点用view替代,用tag设置索引(其实可以设置图片,用数组存起来索引) CGFloat dotSpace = (self.frame.size.width - kCol * kDotSize) / (kCol + 1); // 点之间的间距 for (int i = 0; i < kRow; i++) { for (int j = 0; j < kCol; j++) { UIView *dotView = [[UIView alloc] initWithFrame:CGRectMake(dotSpace + (kDotSize + dotSpace) * j, kBoardTop + dotSpace + (kDotSize + dotSpace) * i, kDotSize, kDotSize)]; dotView.backgroundColor = [UIColor lightGrayColor]; // 初始颜色 dotView.tag = (i * kCol + j) + 1000; // 索引 dotView.layer.cornerRadius = kDotSize / 2; // 切成圆形 [self addSubview:dotView]; } } }
#pragma mark - 触摸事件 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 清除之前的轨迹 [lineArray removeAllObjects]; // 把所有的点状态重置 [gestureDotIndexArray removeAllObjects]; for (int i = 0; i < kRow * kCol; i++) { UIView *dotView = [self viewWithTag:i + 1000]; dotView.userInteractionEnabled = YES; dotView.backgroundColor = [UIColor lightGrayColor]; } // 获取第一个点 startPoint = [touches.anyObject locationInView:self]; // 判断如果在某个dot里面就开始记录 for (int i = 0; i < kRow * kCol; i++) { UIView *dotView = [self viewWithTag:i + 1000]; if (CGRectContainsPoint(dotView.frame, startPoint)) { // 第一个点选中了 isStartDotSelected = YES; // 如果在里面就标记 dotView.backgroundColor = [UIColor greenColor]; dotView.userInteractionEnabled = NO; // 可以用其他的标志字,这里就简单用这个属性好了 // 更改起始点为中心 startPoint = dotView.center; // dot添加到轨迹 [gestureDotIndexArray addObject:[NSNumber numberWithInt:i]]; } } } - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 终止点 endPoint = [touches.anyObject locationInView:self]; // 一定在起始点选中的基础上才有轨迹 if (isStartDotSelected) { // 临时轨迹 tempLine = [UIBezierPath bezierPath]; [tempLine moveToPoint:startPoint]; [tempLine addLineToPoint:endPoint]; #ifdef check_intersect // 判断与之前的线段是否相交,不算最近的一个有接点的线段(目前体验不够好) for (int i = 0; lineArray.count > 0 && i < lineArray.count - 1; i++) { UIBezierPath *path = lineArray[i]; // 得到线段端点数组 NSArray *tempLinePoints = [self getPointsFromPath:tempLine]; NSArray *pathPoints = [self getPointsFromPath:path]; // array里面都是元数据,value转成point,因为array里面只能存value NSValue *value1 = tempLinePoints.firstObject; CGPoint p1 = [value1 CGPointValue]; NSValue *value2 = tempLinePoints.lastObject; CGPoint p2 = [value2 CGPointValue]; NSValue *value3 = pathPoints.firstObject; CGPoint p3 = [value3 CGPointValue]; NSValue *value4 = pathPoints.lastObject; CGPoint p4 = [value4 CGPointValue]; // 相交测试 if (checkLineIntersection(p1, p2, p3, p4)) { [self shakeAnimationForView:tipLabel]; [self resetGesture]; } } #endif // 判断终点是否在dot里面,并且这个点没有划过 for (int i = 0; i < kRow * kCol; i++) { UIView *dotView = [self viewWithTag:i + 1000]; // 必须两个条件一起判保证点不会重入 if (CGRectContainsPoint(dotView.frame, endPoint) && dotView.userInteractionEnabled) { // 如果在里面就标记 dotView.backgroundColor = [UIColor colorWithRed:(arc4random() % 256) / 256.0f green:(arc4random() % 256) / 256.0f blue:(arc4random() % 256) / 256.0f alpha: 1]; dotView.userInteractionEnabled = NO; // 可以用其他的标志字,这里就简单用这个属性好了 // dot添加到轨迹 [gestureDotIndexArray addObject:[NSNumber numberWithInt:i]]; // 重新规划路径 UIBezierPath *settledLine = [[UIBezierPath alloc] init]; [settledLine moveToPoint:startPoint]; [settledLine addLineToPoint:dotView.center]; // 存储路径 [lineArray addObject:settledLine]; // 此处判断一下线路是否相交 // 修改起始点 startPoint = dotView.center; } } } // 重绘 [self setNeedsDisplay]; } - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 处理密码 [self processPassword]; // 最后清除存储的密码轨迹 [self resetGesture]; // 重绘 [self setNeedsDisplay]; }
#pragma mark - 绘制 - (void)drawRect:(CGRect)rect { // 绘制临时路径 tempLine.lineWidth = 5; tempLine.lineJoinStyle = kCGLineJoinRound; [[UIColor redColor] set]; [tempLine stroke]; // 绘制轨迹 for (UIBezierPath *path in lineArray) { path.lineWidth = 5; path.lineJoinStyle = kCGLineJoinRound; [[UIColor blueColor] set]; [path stroke]; } }
(2)密码处理
密码可以是字符串,存到本地,或者用于二次加密
#pragma mark - 处理手势得到的密码 - (void)processPassword { // 得到密码 NSMutableString *passwordStr = [[NSMutableString alloc] init]; for (NSNumber *indexNumber in gestureDotIndexArray) { [passwordStr appendString:[NSString stringWithFormat:@"%d", indexNumber.intValue]]; } switch (_gestureState) { case CREATE_STATE: { pwdSetCount++; if (pwdSetCount == 1) { // 密码存文件(或者全局变量) [[NSUserDefaults standardUserDefaults] setObject:passwordStr forKey:kPasswordKey]; [[NSUserDefaults standardUserDefaults] synchronize]; tipLabel.text = @"请再输一次"; [self shakeAnimationForView:tipLabel]; } else if (pwdSetCount == kPwdCount) { // 检验跟第一次是否一样 NSString *originalPwd = [[NSUserDefaults standardUserDefaults] objectForKey:kPasswordKey]; if ([passwordStr isEqualToString:originalPwd]) { if (self.passwordSetBlock) { self.passwordSetBlock([NSString stringWithFormat:@"password created: %@", passwordStr]); } [self hide]; } else { tipLabel.text = @"密码校验与第一次不同,重新输入"; [self shakeAnimationForView:tipLabel]; pwdSetCount--; // 减回去 } } } break; case VERIFY_STATE: { // 校验密码 NSString *originalPwd = [[NSUserDefaults standardUserDefaults] objectForKey:kPasswordKey]; if ([passwordStr isEqualToString:originalPwd]) { if (self.passwordSetBlock) { self.passwordSetBlock(@"password verify success!"); } [self hide]; } else { if (self.passwordSetBlock) { self.passwordSetBlock(@"password verify failed!"); } tipLabel.text = @"密码校验失败,重新输入"; [self shakeAnimationForView:tipLabel]; } } break; default: break; } }
(3)线段相交检测
如果要求画出的手势路线不能相交,需要线段相交算法,从线段获得两个端点的坐标
#pragma mark - 线段相交测试(p1,p2是线段1的端点,p3,p4是线段2的端点) bool checkLineIntersection(CGPoint p1, CGPoint p2, CGPoint p3, CGPoint p4) { CGFloat denominator = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y); // In this case the lines are parallel so we assume they don't intersect~ if (denominator <= (1e-6) && denominator >= -(1e-6)) { return true; } // amazing~ CGFloat ua = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denominator; CGFloat ub = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denominator; if (ua >= 0.0f && ua <= 1.0f && ub >= 0.0f && ub <= 1.0f) { return true; } return false; }
#pragma mark - 从贝塞尔曲线上得到点列表 // http://stackoverflow.com/questions/3051760/how-to-get-a-list-of-points-from-a-uibezierpath - (NSMutableArray *)getPointsFromPath:(UIBezierPath *)path { CGPathRef pathCGPath = path.CGPath; NSMutableArray *bezierPoints = [NSMutableArray array]; CGPathApply(pathCGPath, (__bridge void * _Nullable)(bezierPoints), MyCGPathApplierFunc); return bezierPoints.copy; } void MyCGPathApplierFunc(void *arrayInfo, const CGPathElement *element) { NSMutableArray *bezierPoints = (__bridge NSMutableArray *)arrayInfo; CGPoint *points = element->points; CGPathElementType type = element->type; switch(type) { case kCGPathElementMoveToPoint: // contains 1 point [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]]; break; case kCGPathElementAddLineToPoint: // contains 1 point [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]]; break; case kCGPathElementAddQuadCurveToPoint: // contains 2 points [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]]; [bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]]; break; case kCGPathElementAddCurveToPoint: // contains 3 points [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]]; [bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]]; [bezierPoints addObject:[NSValue valueWithCGPoint:points[2]]]; break; case kCGPathElementCloseSubpath: // contains no point break; } }
源代码下载
csdn:手势解锁github:手势解锁
相关文章推荐
- iOS开发:手势解锁(带路线相交检测)
- iOS 开发实战-锁屏界面(手势解锁)
- iOS开发之应用内检测手机锁屏,解锁状态
- iOS开发 手势密码解锁和指纹TouchID解锁
- iOS开发UI篇—实现一个简单的手势解锁应用(基本) - 文顶顶
- iOS开发UI篇—实现一个简单的手势解锁应用(基本)
- iOS开发-检测程序在前台和后台锁屏解锁的状态
- iOS开发Quartz2D十二:手势解锁实例
- iOS开发 - 19.手势解锁
- iOS开发之应用内检测手机锁屏,解锁状态
- iOS开发之手势解锁
- iOS开发之应用内检测手机是否为锁屏,解锁状态
- 【iOS开发-87】怎么实现支付宝的手势解锁效果?利用touches3个方法和drawRect方法
- iOS开发之应用内检测手机锁屏,解锁状态
- iOS开发:自定义控件实现手势解锁
- iOS开发-进入后台后打开指定页面,如手势解锁
- iOS开发之应用内检测手机锁屏,解锁状态
- iOS--开发之手势解锁
- iOS开发:处理多点触摸与手势检测
- iOS开发UI篇—实现一个简单的手势解锁应用(基本)