iOS - TouchLock 手势锁

1、绘制手势锁

  • 具体实现代码见 GitHub 源码 QExtension
  • QTouchLockView.h
        @interface QTouchLockView : UIView
    
        /// 提示信息框
        @property (nonatomic, strong) UILabel *alertLabel;
    
        /**
         *  创建手势锁视图控件,获取滑动手势结果
         *
         *  @param frame    手势锁视图控件的位置尺寸
         *  @param result   滑动手势结果,YES 成功,NO 失败
         *
         *  @return 手势锁视图控件
         */
        + (instancetype)q_touchLockViewWithFrame:(CGRect)frame
                                      pathResult:(void (^)(BOOL isSucceed, NSString *result))result;
    
        @end
  • QTouchLockView.m
        #import "NSString+Hash.h"
    
        @interface QTouchLockView ()
    
        /// 存放选中的按钮
        @property (nonatomic, strong) NSMutableArray *selectedArray;
    
        /// 当前被选中的按钮
        @property (nonatomic, assign) CGPoint currentPoint;
    
        /// 滑动手势结果
        @property (nonatomic, copy) void (^resultBlock)(BOOL, NSString *);
    
        @end
    
        @implementation QTouchLockView
    
        /// 创建手势锁界面,获取滑动结果
        + (instancetype)q_touchLockViewWithFrame:(CGRect)frame
                                      pathResult:(void (^)(BOOL isSucceed, NSString *result))result {
    
            QTouchLockView *touchLockView = [[self alloc] init];
    
            CGRect tmpFrame = frame;
            tmpFrame.size.height = frame.size.width;
    
            touchLockView.frame = tmpFrame;
            touchLockView.resultBlock = result;
    
            return touchLockView;
        }
    
        /// 初始化界面
        - (instancetype)initWithFrame:(CGRect)frame {
    
            if (self = [super initWithFrame:frame]) {
    
                self.backgroundColor = [UIColor whiteColor];
    
                // 添加提示信息框
                self.alertLabel = [[UILabel alloc] init];
                self.alertLabel.textAlignment = NSTextAlignmentCenter;
                self.alertLabel.textColor = [UIColor redColor];
                self.alertLabel.backgroundColor = [UIColor clearColor];
                self.alertLabel.numberOfLines = 1;
                self.alertLabel.adjustsFontSizeToFitWidth = YES;
                [self addSubview:self.alertLabel];
    
                // 添加按钮
                for (int i = 0; i < 9; i++){
    
                    UIButton *btn = [[UIButton alloc] init];
    
                    NSString *bundlePath = [[[NSBundle mainBundle] resourcePath]
                                            stringByAppendingPathComponent:@"QTouchLockView.bundle"];
    
                    UIImage *normalImage = [UIImage imageWithContentsOfFile:
                                            [bundlePath stringByAppendingPathComponent:@"gesture_node_normal"]];
                    UIImage *selectedImage = [UIImage imageWithContentsOfFile:
                                              [bundlePath stringByAppendingPathComponent:@"gesture_node_selected"]];
                    UIImage *highlightedImage = [UIImage imageWithContentsOfFile:
                                                 [bundlePath stringByAppendingPathComponent:@"gesture_node_highlighted"]];
    
                    [btn setBackgroundImage:normalImage forState:UIControlStateNormal];
                    [btn setBackgroundImage:selectedImage forState:UIControlStateSelected];
                    [btn setBackgroundImage:highlightedImage forState:UIControlStateHighlighted];
    
                    // 设置 tag 值,设置按钮对应的密码值
                    btn.tag = i + 1;
    
                    // 关闭按钮的交互,响应触摸事件
                    btn.userInteractionEnabled = NO;
    
                    [self addSubview:btn];
                }
            }
            return self;
        }
    
        /// 布局控件
        - (void)layoutSubviews {
            [super layoutSubviews];
    
            // 设置按钮的 frame
            for (int i = 0; i < self.subviews.count - 1; i++) {
    
                // 列数
                NSInteger cols = 3;
    
                // 设置按钮尺寸
                CGFloat W = self.bounds.size.width;
                CGFloat H = self.bounds.size.height;
                CGFloat btnW = W / 5;
                CGFloat btnH = H / 5;
    
                // 计算按钮的 x 坐标值
                NSUInteger col = i % cols;
                CGFloat btnX = col * btnW * 2;
    
                // 计算按钮的 y 坐标值
                NSUInteger row = i / cols;
                CGFloat btnY = row * btnH * 2;
    
                // 设置按钮的 frame
                UIButton *btn = self.subviews[i + 1];
                btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
            }
    
            // 设置提示信息框的 frame
            self.alertLabel.frame = CGRectMake(0, -50, self.bounds.size.width, 30);
        }
    
        /// 触摸开始
        - (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            // 获取触摸起始点位置
            CGPoint startPoint = [touches.anyObject locationInView:self];
    
            // 获取其 button
            UIButton *button = nil;
            for (UIButton *btn in self.subviews) {
    
                // 设置触摸按钮的灵敏度
                CGRect frame = btn.frame;
                CGRect tmpFrame = CGRectMake(frame.origin.x + frame.size.width / 4,
                                             frame.origin.y + frame.size.height / 4,
                                             frame.size.width / 2,
                                             frame.size.height / 2);
    
                // 判断某点在不在其 frame 上
                if (CGRectContainsPoint(tmpFrame, startPoint)) {
                    button = btn;
                }
            }
    
            // 选中此 button
            if (button && button.selected == NO) {
                button.selected = YES;
                [self.selectedArray addObject:button];
            }
        }
    
        /// 触摸移动
        - (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            // 获取触摸点位置
            CGPoint touchPoint = [touches.anyObject locationInView:self];
    
            // 获取其 button
            UIButton *button = nil;
            for (UIButton *btn in self.subviews) {
    
                // 设置触摸按钮的灵敏度
                CGRect frame = btn.frame;
                CGRect tmpFrame = CGRectMake(frame.origin.x + frame.size.width / 4,
                                             frame.origin.y + frame.size.height / 4,
                                             frame.size.width / 2,
                                             frame.size.height / 2);
    
                // 判断某点在不在其 frame 上
                if (CGRectContainsPoint(tmpFrame, touchPoint)) {
                    button = btn;
                }
            }
    
            // 选中此 button
            if (button && button.selected == NO) {
                button.selected = YES;
                [self.selectedArray addObject:button];
            } else {
                self.currentPoint = touchPoint;
            }
    
            // 刷新视图
            [self setNeedsDisplay];
        }
    
        /// 触摸结束
        - (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            if (self.selectedArray.count < 4) {
    
                // 触摸点数过少
                if (self.resultBlock) {
                    self.resultBlock(NO, @"请至少连续连接四个点");
                }
    
                for (UIButton *btn in self.selectedArray) {
    
                    btn.highlighted = YES;
                    btn.selected = NO;
                }
    
                // 延迟
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                                             (int64_t)(0.5 * NSEC_PER_SEC)),
                               dispatch_get_main_queue(), ^{
    
                                   [self clearPath];
                               });
    
            } else {
    
                // 触摸完成
                if (self.resultBlock) {
    
                    // 获取触摸结果
                    NSMutableString *path = [NSMutableString string];
                    for (UIButton *btn in self.selectedArray) {
                        [path appendFormat:@"%ld", btn.tag];
                    }
    
                    // 对滑动获取的密码值进行 MD5 加密
                    NSString *md5Path = [path q_md5String];
    
                    self.resultBlock(YES, md5Path);
                }
    
                [self clearPath];
            }
        }
    
        /// 触摸取消
        - (void)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            [self touchesEnded:touches withEvent:event];
        }
    
        /// 绘制贝塞尔连接线
        - (void)drawRect:(CGRect)rect {
    
            if (self.selectedArray.count == 0) {
                return;
            }
    
            UIBezierPath *path = [UIBezierPath bezierPath];
            path.lineWidth = 5;
            path.lineJoinStyle = kCGLineCapRound;
            [[UIColor colorWithRed:1 green:0 blue:0 alpha:0.5] set];
    
            for (int i = 0; i < self.selectedArray.count; i++) {
    
                UIButton *btn = self.selectedArray[i];
    
                // 如果是第一个按钮,则将其曲线的起点放在其按钮上,否则则进行连线
                if (i == 0) {
                    [path moveToPoint:btn.center];
                } else {
                    [path addLineToPoint:btn.center];
                }
            }
    
            // 如果不满足上述条件,按钮不存在则连接到临时点
            [path addLineToPoint:self.currentPoint];
            [path stroke];
        }
    
        /// 清除连接线
        - (void)clearPath {
    
            // 取消选中按钮
            for (UIButton *btn in self.selectedArray) {
                btn.highlighted = NO;
                btn.selected = NO;
            }
    
            // 清空选中按钮
            [self.selectedArray removeAllObjects];
    
            // 刷新视图
            [self setNeedsDisplay];
        }
    
        /// 懒加载
        - (NSMutableArray *)selectedArray {
            if (_selectedArray == nil) {
                _selectedArray = [NSMutableArray array];
            }
            return _selectedArray;
        }
    
        @end
  • ViewController.m
        // 设置 frame
        CGFloat margin = 50;
        CGFloat width = self.view.bounds.size.width - margin * 2;
        CGRect frame = CGRectMake(margin, 200, width, width);
    
        // 创建手势锁视图界面,获取滑动结果
        QTouchLockView *touchLockView = [QTouchLockView q_touchLockViewWithFrame:frame
                                                                      pathResult:^(BOOL isSucceed, NSString * _Nonnull result) {
    
            // 处理手势触摸结果
            [self dealTouchResult:result isSucceed:isSucceed];
        }];
    
        [self.view addSubview:touchLockView];
    
        - (void)dealTouchResult:(NSString *)result isSucceed:(BOOL)isSucceed {
    
            // 处理手势触摸结果
    
            if (isSucceed) {
    
                // 判读密码是否存在
                NSUserDefaults *df = [NSUserDefaults standardUserDefaults];
    
                if ([df objectForKey:@"touchLock"] == nil) {
    
                    // 设置手势锁
    
                    [self.passWordArrM addObject:result];
    
                    if (self.passWordArrM.count == 1) {
                        self.touchLockView.alertLabel.text = @"请再设置一次";
                    }
    
                    if (self.passWordArrM.count == 2) {
                        if ([self.passWordArrM[0] isEqualToString:self.passWordArrM[1]]) {
    
                            // 存储密码
                            [df setValue:self.passWordArrM[0] forKey:@"touchLock"];
                            [df synchronize];
    
                            self.touchLockView.alertLabel.text = @"手势密码设置成功";
    
                        } else {
    
                            // 两次滑动结果不一致
                            [self.passWordArrM removeAllObjects];
    
                            self.touchLockView.alertLabel.text = @"两次滑动的结果不一致,请重新设置";
                        }
                    }
    
                } else {
    
                    // 解锁
    
                    if ([result isEqualToString:[df objectForKey:@"touchLock"] ]) {
                        self.touchLockView.alertLabel.text = @"解锁成功";
                    } else {
                        self.touchLockView.alertLabel.text = @"密码不正确,请重试";
                    }
                }
    
            } else {
    
                // 滑动点数过少
                self.touchLockView.alertLabel.text = result;
            }
        }
  • 效果

时间: 2024-07-29 01:30:24

iOS - TouchLock 手势锁的相关文章

方法-IOS 触摸 手势和tableView cell的点击冲突

问题描述 IOS 触摸 手势和tableView cell的点击冲突 刚开始 书写的方法 // 触摸 (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //取出touches集合元素 UITouch *touch = [touches anyObject]; NSLog(@"%@", touch); CGPoint point = [touch locationInView:self.view]; // 打

大数据比手势锁靠谱?支付宝回应质疑

文章讲的是大数据比手势锁靠谱?支付宝回应质疑,7月8日,支付宝更新到最新的9.0版本,带来众多新功能,增强了社交属性.但是,以往大家使用频率最高的手势解锁功能却要被强制关闭,这让许多用户大呼没有安全感.虽然关闭手势之后,会有来自众安保险,赔付额度为100万元的不限次数保险,依然有很多用户表示:没有手势解锁隐私怎么保护,不要逼我卸载!支付宝一时间被推到了舆论的风口浪尖. 支付宝如何回应? 支付宝在9日晚间发表微博回应手势密码取消问题,表示支付宝有多层保障,就算手机丢失支付宝依然安全.支付宝拥有大数

谈谈iOS中的锁

谈谈iOS中的锁(解析一下NSLock) 1 前言 近日工作不是太忙,刚好有时间了解一些其他东西,本来打算今天上午去体检,但是看看天气还是明天再去吧,也有很大一个原因:就是周六没有预约上!闲话少说,这里简单对锁来个简单介绍分享. 2 目录 第一部分:什么是锁 第二部分:锁的分类 第三部分:锁的作用 第四部分:iOS中锁的实现 ##### 第一部分:什么是锁 从小就知道锁,就是家里门上的那个锁,用来防止盗窃的锁.它还有钥匙,用于开锁.不过这里的锁,并不是小时候认知的锁,而是站在程序员的角度的锁.这

超实用的Android手势锁制作实例教程_Android

今天偶遇以github上gesturelock关于手势锁的一个例子(有兴趣的去搜索下看看),于是下载下来研究,无奈基本没有注释,代码上存在一些问题(当设置gravity=center_vertical无法进行手势选择,无意中发现的),于是借鉴这位仁兄的代码,自己重写写了一个,修复了一些问题,加入一些基本的自定义属性,在此先感谢这位兄弟~. 先上图,默认效果图: 当然可以自定义数量啊,颜色神马的,自定义效果图: 如果你有艺术细胞,可以给我推荐几个颜色,无奈个人审美有问题~ 1.整体思路a.自定义了

超实用的Android手势锁制作实例教程

今天偶遇以github上gesturelock关于手势锁的一个例子(有兴趣的去搜索下看看),于是下载下来研究,无奈基本没有注释,代码上存在一些问题(当设置gravity=center_vertical无法进行手势选择,无意中发现的),于是借鉴这位仁兄的代码,自己重写写了一个,修复了一些问题,加入一些基本的自定义属性,在此先感谢这位兄弟~. 先上图,默认效果图: 当然可以自定义数量啊,颜色神马的,自定义效果图: 如果你有艺术细胞,可以给我推荐几个颜色,无奈个人审美有问题~ 1.整体思路 a.自定义

iOS实现手势解锁操作_IOS

本文主要介绍通过手势识别实现手势解锁功能,这个方法被广泛用于手机解锁,密码验证,快捷支付等功能实现.事例效果如下所示.  首先,我们先分析功能的实现过程,首先我们需要先看大致的实现过程: 1.加载九宫格页面 2.实现按钮被点击及滑动过程中按钮状态的改变 3.实现滑动过程中的连线 4.绘制完毕后判定密码是否正确, 5.密码判定后实现跳转. 下面我们就来用代码实现上述五个过程. 1.加载九宫格界面 1.1九宫格内控件的分布 3*3 ,我们可以自定义view(包含3*3个按钮),添加到viewCont

ios的手势操作之UIGestureRecognizer浅析(推荐)_IOS

一.概述 iPhone中处理触摸屏的操作,在3.2之前是主要使用的是由UIResponder而来的如下4种方式: - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)

基于JS实现Android,iOS一个手势动画效果_javascript技巧

废话不多说了,先给大家展示下效果图: 这是iOS下的效果,android下完全一致.通过do_GestureView组件和do_Animation组件,deviceone能很容易实现复杂的跨平台纯原生动画效果,这个示例就是通过手势控制图片上下动画滑动实现开合效果,还支持声音效果. 下面是主要的代码 //index.ui.js var do_Animator1 = mm("do_Animator"); do_Animator1.append(500, { y: -1334, curve:

IOS各种手势的使用

#import "HYBRootViewController.h" @interface HYBRootViewController () <UIGestureRecognizerDelegate> @property (nonatomic, retain) UIImageView *imageView; - (void)imageViewTap:(UITapGestureRecognizer *)sender; @end @implementation HYBRootVi