iOS - TouchLock 手势锁

简介: 1、绘制手势锁具体实现代码见 GitHub 源码 QExtensionQTouchLockView.h @interface QTouchLockView : UIView /// 提示信息框 @property (nonatomic, strong) UILabe...

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;
            }
        }
  • 效果

    lockView1lockView3

目录
相关文章
|
iOS开发
(六)IOS手势和触摸的用法
(六)IOS手势和触摸的用法
191 0
|
iOS开发
如何使用iOS手势UIGestureRecognizer
如何使用iOS手势UIGestureRecognizer
74 0
|
API iOS开发
iOS手势与变形
手势在用户交互中有着举足轻重的作用,这篇文字简单的介绍了iOS中的手势,并通过手势对控件进行变形处理。
88 0
|
数据安全/隐私保护 iOS开发
iOS 手势密码锁
首先看一下效果
99 0
|
iOS开发
(七) IOS 响应者链和手势
(七) IOS 响应者链和手势
315 0
|
设计模式 iOS开发
iOS手势全埋点
iOS手势全埋点
201 0
iOS手势全埋点
|
iOS开发
iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用(二)
iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用
303 0
|
iOS开发
iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用(一)
iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用
196 0
iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用(一)