美文网首页
九宫格手势密码

九宫格手势密码

作者: DDY | 来源:发表于2018-01-10 11:56 被阅读323次
    Lock.png

    上图是QQ的解锁画面,不知道这样的设计是故意为之还是疏忽,允许隔断连线确实增加了密码复杂性,但是在QQ这里出现了有时候快速划过连不上的问题。。。

    所以最好还是采用不允许隔点连线为好。

    首先画圆圈

    DDYCircleView.h

    #import <UIKit/UIKit.h>
    
    /** 圆的状态 */
    typedef NS_ENUM(NSInteger, DDYCircleViewState) {
        DDYCircleViewStateNormal             = 1,
        DDYCircleViewStateSelected           = 2,
        DDYCircleViewStateError              = 3,
        DDYCircleViewStateLastOneSelected    = 4,
        DDYCircleViewStateLastOneError       = 5,
    };
    
    /** 圆的用途 */
    typedef NS_ENUM(NSInteger, DDYCircleViewType) {
        DDYCircleViewTypeInfo                = 1,
        DDYCircleViewTypeGesture             = 2,
    };
    
    @interface DDYCircleView : UIView
    
    /** 所处状态 */
    @property (nonatomic, assign) DDYCircleViewState state;
    /** 类型 */
    @property (nonatomic, assign) DDYCircleViewType type;
    /** 是否有箭头 默认YES */
    @property (nonatomic, assign) BOOL arrow;
    /** 角度 */
    @property (nonatomic, assign) CGFloat angle;
    /** 初始化 */
    + (instancetype)circleView;
    
    @end
    

    DDYCircleView.m

    #import "DDYCircleView.h"
    
    @implementation DDYCircleView
    
    + (instancetype)circleView {
        return [[self alloc] init];
    }
    
    - (instancetype)init {
        if (self = [super init]) {
            self.backgroundColor = lockViewBgColor;
        }
        return self;
    }
    
    - (void)drawRect:(CGRect)rect {
        
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        // 上下文旋转
        [self tansformCtx:ctx rect:rect];
        // 画外空心圆
        [self drawOutCircleWithCtx:ctx rect:rect];
        // 画内实心圆
        [self drawInCircleWithCtx:ctx rect:rect];
        // 画三角形
        [self drawTrangleWithCtx:ctx rect:rect];
    }
    
    #pragma mark 上下文旋转
    - (void)tansformCtx:(CGContextRef)ctx rect:(CGRect)rect {
        CGFloat translateXY = rect.size.width*.5f;
        CGContextTranslateCTM(ctx, translateXY, translateXY);
        CGContextRotateCTM(ctx, self.angle);
        CGContextTranslateCTM(ctx, -translateXY, -translateXY);
    }
    
    #pragma mark 画外空心圆
    - (void)drawOutCircleWithCtx:(CGContextRef)ctx rect:(CGRect)rect {
        CGRect circleRect = DDYRect(outCircleWidth, outCircleWidth, rect.size.width-2*outCircleWidth, rect.size.height-2*outCircleWidth);
        CGMutablePathRef circlePath = CGPathCreateMutable();
        CGPathAddEllipseInRect(circlePath, NULL, circleRect);
        CGContextAddPath(ctx, circlePath);
        [[self outCircleColor] set];
        CGContextSetLineWidth(ctx, outCircleWidth);
        CGContextStrokePath(ctx);
        CGPathRelease(circlePath);
    }
    
    #pragma mark 画内实心圆
    - (void)drawInCircleWithCtx:(CGContextRef)ctx rect:(CGRect)rect {
        CGFloat radio = (self.type == DDYCircleViewTypeGesture) ? circleRadio : 1;
        CGFloat circleX = rect.size.width/2 * (1-radio) + outCircleWidth;
        CGFloat circleY = rect.size.height/2 * (1-radio) + outCircleWidth;
        CGFloat circleW = rect.size.width*radio - outCircleWidth*2;
        CGFloat circleH = rect.size.height*radio - outCircleWidth*2;
        CGMutablePathRef circlePath = CGPathCreateMutable();
        CGPathAddEllipseInRect(circlePath, NULL, CGRectMake(circleX, circleY, circleW, circleH));
        [[self inCircleColor] set];
        CGContextAddPath(ctx, circlePath);
        CGContextFillPath(ctx);
        CGPathRelease(circlePath);
    }
    
    #pragma mark 画三角形
    - (void)drawTrangleWithCtx:(CGContextRef)ctx rect:(CGRect)rect {
        if (self.arrow) {
            CGPoint topPoint = CGPointMake(rect.size.width/2, 10);
            CGMutablePathRef trianglePath = CGPathCreateMutable();
            CGPathMoveToPoint(trianglePath, NULL, topPoint.x, topPoint.y);
            CGPathAddLineToPoint(trianglePath, NULL, topPoint.x - trangleLength/2, topPoint.y + trangleLength/2);
            CGPathAddLineToPoint(trianglePath, NULL, topPoint.x + trangleLength/2, topPoint.y + trangleLength/2);
            CGContextAddPath(ctx, trianglePath);
            [[self trangleColor] set];
            CGContextFillPath(ctx);
            CGPathRelease(trianglePath);
        }
    }
    
    #pragma mark 外圆颜色
    - (UIColor *)outCircleColor {
        UIColor *color;
        switch (self.state) {
            case DDYCircleViewStateNormal:
                color = outCircleNormalColor;
                break;
            case DDYCircleViewStateSelected:
                color = outCircleSelectedColor;
                break;
            case DDYCircleViewStateError:
                color = outCircleErrorColor;
                break;
            case DDYCircleViewStateLastOneSelected:
                color = outCircleSelectedColor;
                break;
            case DDYCircleViewStateLastOneError:
                color = outCircleErrorColor;
                break;
            default:
                color = outCircleNormalColor;
                break;
        }
        return color;
    }
    
    #pragma mark 内圆颜色
    - (UIColor *)inCircleColor {
        UIColor *color;
        switch (self.state) {
            case DDYCircleViewStateNormal:
                color = inCircleNormalColor;
                break;
            case DDYCircleViewStateSelected:
                color = inCircleSelectedColor;
                break;
            case DDYCircleViewStateError:
                color = inCircleErrorColor;
                break;
            case DDYCircleViewStateLastOneSelected:
                color = inCircleSelectedColor;
                break;
            case DDYCircleViewStateLastOneError:
                color = inCircleErrorColor;
                break;
            default:
                color = inCircleNormalColor;
                break;
        }
        return color;
    }
    
    #pragma mark 三角颜色
    - (UIColor *)trangleColor {
        UIColor *color;
        switch (self.state) {
            case DDYCircleViewStateNormal:
                color = trangleNormalColor;
                break;
            case DDYCircleViewStateSelected:
                color = trangleSelectedColor;
                break;
            case DDYCircleViewStateError:
                color = trangleErrorColor;
                break;
            case DDYCircleViewStateLastOneSelected:
                color = trangleSelectedColor;
                break;
            case DDYCircleViewStateLastOneError:
                color = trangleErrorColor;
                break;
            default:
                color = trangleNormalColor;
                break;
        }
        return color;
    }
    
    #pragma mark 角度set方法
    - (void)setAngle:(CGFloat)angle {
        _angle = angle;
        [self setNeedsDisplay];
    }
    
    #pragma mark 状态set方法
    - (void)setState:(DDYCircleViewState)state {
        _state = state;
        [self setNeedsDisplay];
    }
    
    @end
    

    其次将上面的圆圈画成九宫格,形成手势区域

    DDYLockView.h

    #import <UIKit/UIKit.h>
    
    @class  DDYLockView;
    
    typedef NS_ENUM(NSInteger, DDYLockViewType) {
        DDYLockViewTypeSetting = 1, // 设置手势密码
        DDYLockViewTypeLogin   = 2, // 登录手势密码
        DDYLockViewTypeVerify  = 3, // 验证手势密码
    };
    
    typedef NS_ENUM(NSInteger, DDYLockViewState) {
        DDYLockViewStateLess        = 1,    // 连线个数少于最小值(设置)
        DDYLockViewStateFirstFinish = 2,    // 提示再次绘制以确认(设置)
        DDYLockViewStateSecondFinish= 3,    // 两次绘制一致可保存(设置)
        DDYLockViewStateSecondError = 4,    // 两次绘制路径不一致(设置)
        DDYLockViewStateLoginFinish = 5,    // 手势密码登录成功(登录)
        DDYLockViewStateLoginError  = 6,    // 手势密码登录失败(登录)
        DDYLockViewStateVerifyFinish= 7,    // 修改密码验证成功(验证)
        DDYLockViewStateVerifyError = 8,    // 修改密码验证失败(验证)
        
    };
    
    //--------------------------- delegate ---------------------------//
    @protocol DDYLockViewDelegate <NSObject>
    
    - (void)lockView:(DDYLockView *)lockView state:(DDYLockViewState)state;
    
    @end
    
    //--------------------------- InfoView ---------------------------//
    @interface DDYLockInfoView : UIView
    
    + (instancetype)infoViewWithFrame:(CGRect)frame;
    
    @end
    
    //--------------------------- lockView ---------------------------//
    @interface DDYLockView : UIView
    
    /** 是否裁剪 默认YES */
    @property (nonatomic, assign) BOOL clip;
    /** 是否有箭头 默认YES */
    @property (nonatomic, assign) BOOL arrow;
    /** 解锁类型 */
    @property (nonatomic, assign) DDYLockViewType type;
    /** 代理 */
    @property (nonatomic, weak) id<DDYLockViewDelegate> delegate;
    /** 初始化 */
    + (instancetype)lockViewWithType:(DDYLockViewType)type;
    
    + (instancetype)lockViewWithType:(DDYLockViewType)type clip:(BOOL)clip arrow:(BOOL)arrow;
    
    - (instancetype)initWithType:(DDYLockViewType)type clip:(BOOL)clip arrow:(BOOL)arrow;
    
    @end
    

    DDYLockView.m

    #import "DDYLockView.h"
    
    //--------------------------- InfoView ---------------------------//
    @implementation DDYLockInfoView
    
    + (instancetype)infoViewWithFrame:(CGRect)frame {
        return [[self alloc] initWithFrame:frame];
    }
    
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            self.backgroundColor = circleBgColor;
            for (int i = 0; i < 9; i++) {
                DDYCircleView *circle = [DDYCircleView circleView];
                circle.type = DDYCircleViewTypeInfo;
                [self addSubview:circle];
            }
        }
        return self;
    }
    
    - (void)layoutSubviews {
        [super layoutSubviews];
        CGFloat itemViewWH = circleInfoRadius * 2;
        CGFloat itemMargin = (self.ddy_w - 3*itemViewWH) / 3.f;
        // 九宫格
        [self.subviews enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
            NSUInteger row = idx % 3;
            NSUInteger col = idx / 3;
            CGFloat x = itemMargin*row + row*itemViewWH + itemMargin/2;
            CGFloat y = itemMargin*col + col*itemViewWH + itemMargin/2;
            circle.tag = idx + 1;
            circle.frame = DDYRect(x, y, itemViewWH, itemViewWH);
        }];
    }
    
    @end
    
    //--------------------------- lockView ---------------------------//
    @interface DDYLockView ()
    /** 选中的圆数组 */
    @property (nonatomic, strong) NSMutableArray <DDYCircleView *>*selectedCircleArray;
    /** 当前点位 */
    @property (nonatomic, assign) CGPoint currentPoint;
    /** 数组清空标识 */
    @property (nonatomic, assign) BOOL isCleaned;
    
    @end
    
    @implementation DDYLockView
    
    #pragma mark setter/getter
    #pragma mark 设置箭头
    - (void)setArrow:(BOOL)arrow {
        _arrow = arrow;
        // 遍历子控件,改变其是否有箭头
        [self.subviews enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
            [circle setArrow:arrow];
        }];
    }
    
    - (NSMutableArray <DDYCircleView *>*)selectedCircleArray {
        if (!_selectedCircleArray) {
            _selectedCircleArray = [NSMutableArray array];
        }
        return _selectedCircleArray;
    }
    
    #pragma mark - 初始化
    #pragma mark 类方法初始化,默认裁剪有箭头
    + (instancetype)lockViewWithType:(DDYLockViewType)type {
        return [[self alloc] initWithType:type clip:YES arrow:YES];
    }
    
    + (instancetype)lockViewWithType:(DDYLockViewType)type clip:(BOOL)clip arrow:(BOOL)arrow {
        return [[self alloc] initWithType:type clip:clip arrow:arrow];
    }
    
    #pragma mark 初始化方法:初始化type、clip、arrow
    - (instancetype)initWithType:(DDYLockViewType)type clip:(BOOL)clip arrow:(BOOL)arrow {
        if (self = [super init]) {
            [self lockViewPrepare];
            self.type  = type;
            self.clip  = clip;
            self.arrow = arrow;
        }
        return self;
    }
    
    - (instancetype)init {
        if (self = [super init]) {
            [self lockViewPrepare];
        }
        return self;
    }
    
    #pragma mark UI
    #pragma mark 视图准备
    - (void)lockViewPrepare {
        self.frame = DDYRect(0, 0, DDYSCREENW-lockViewEdgeMargin*2, DDYSCREENW-lockViewEdgeMargin*2);
        self.center = DDYPoint(DDYSCREENW/2, DDYSCREENH*3/5);
        self.clip = YES;  // 默认裁剪
        self.arrow = YES; // 默认有箭头
        self.backgroundColor = circleBgColor;
        for (int i = 0; i < 9; i++) {
            DDYCircleView *circle = [DDYCircleView circleView];
            circle.type = DDYCircleViewTypeGesture;
            circle.arrow = self.arrow;
            [self addSubview:circle];
        }
    }
    
    #pragma mark 布局子控件
    - (void)layoutSubviews {
        [super layoutSubviews];
        CGFloat itemViewWH = circleRadius * 2;
        CGFloat itemMargin = (self.ddy_w - 3*itemViewWH) / 3.f;
        // 九宫格
        [self.subviews enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
            NSUInteger row = idx % 3;
            NSUInteger col = idx / 3;
            CGFloat x = itemMargin*row + row*itemViewWH + itemMargin/2;
            CGFloat y = itemMargin*col + col*itemViewWH + itemMargin/2;
            circle.tag = idx + 1;
            circle.frame = DDYRect(x, y, itemViewWH, itemViewWH);
        }];
    }
    
    #pragma mark - touch began / touch moved / touch end
    #pragma mark touch began
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // 手势重置
        [self gestureReset];
        self.currentPoint = CGPointZero;
        UITouch *touch = [touches anyObject];
        CGPoint point = [touch locationInView:self];
        [self.subviews enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
            if (CGRectContainsPoint(circle.frame, point)) {
                circle.state = DDYCircleViewStateSelected;
                [self.selectedCircleArray addObject:circle];
            }
        }];
        // 数组中最后一个对象的处理
        [self.selectedCircleArray lastObject].state = DDYCircleViewStateLastOneSelected;
        [self setNeedsDisplay];
    }
    
    #pragma mark touch moved
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        self.currentPoint = CGPointZero;
        UITouch *touch = [touches anyObject];
        CGPoint point = [touch locationInView:self];
        [self.subviews enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
            if (CGRectContainsPoint(circle.frame, point)) {
                if (![self.selectedCircleArray containsObject:circle]) {
                    [self.selectedCircleArray addObject:circle];
                    // move过程中连线(包含跳跃连线的处理)
                    [self calculateAngleAndConnectJumpedCircle];
                }
            } else {
                self.currentPoint = point;
            }
        }];
        
        [self.selectedCircleArray enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
            circle.state = DDYCircleViewStateSelected;
            // 如果是登录或验证密码需要改相应状态
            if (self.type == DDYLockViewTypeSetting) {
                circle.state = DDYCircleViewStateLastOneSelected;
            }
        }];
        // 数组中最后一个对象的处理
        [self.selectedCircleArray lastObject].state = DDYCircleViewStateLastOneSelected;
        [self setNeedsDisplay];
    }
    
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        self.isCleaned = NO;
        // 拼手势密码字符串
        NSMutableString *gesture = [NSMutableString string];
        for (DDYCircleView *circle in self.selectedCircleArray) {
            [gesture appendFormat:@"%@", @(circle.tag)];
        }
        CGFloat length = [gesture length];
        if (length == 0) return;
        
        if ([self.delegate respondsToSelector:@selector(lockView:state:)]) {
            // 手势绘制结果处理
            switch (self.type) {
                case DDYLockViewTypeSetting:
                {
                    if (length < lockCircleLeast) {
                        [self.delegate lockView:self state:DDYLockViewStateLess];
                        [self changeCircleInSelectedCircleArrayWithSate:DDYCircleViewStateError];
                    } else if ([DDYUserDefaultsGet(lockOneKey) length] < lockCircleLeast) {
                        DDYUserDefaultsSet(gesture, lockOneKey);
                        [self.delegate lockView:self state:DDYLockViewStateFirstFinish];
                    } else if ([gesture isEqual:DDYUserDefaultsGet(lockOneKey)]) {
                        DDYUserDefaultsSet(gesture, lockEndKey);
                        [self.delegate lockView:self state:DDYLockViewStateSecondFinish];
                    } else {
                        [self.delegate lockView:self state:DDYLockViewStateSecondError];
                        [self changeCircleInSelectedCircleArrayWithSate:DDYCircleViewStateError];
                        DDYUserDefaultsSet(nil, lockOneKey);
                    }
                }
                    break;
                case DDYLockViewTypeLogin:
                {
                    if ([gesture isEqual:DDYUserDefaultsGet(lockEndKey)]) {
                        [self.delegate lockView:self state:DDYLockViewStateLoginFinish];
                    } else {
                        [self.delegate lockView:self state:DDYLockViewStateLoginError];
                        [self changeCircleInSelectedCircleArrayWithSate:DDYCircleViewStateError];
                    }
                }
                    break;
                case DDYLockViewTypeVerify:
                {
                    if ([gesture isEqual:DDYUserDefaultsGet(lockEndKey)]) {
                        [self.delegate lockView:self state:DDYLockViewStateVerifyFinish];
                    } else {
                        [self.delegate lockView:self state:DDYLockViewStateVerifyError];
                        [self changeCircleInSelectedCircleArrayWithSate:DDYCircleViewStateError];
                    }
                }
                    break;
            }
        }
        [self errorToDisplay];
    }
    
    #pragma mark - 私有操作方法
    #pragma mark 手势清空重置操作
    - (void)gestureReset {
        // 线程安全
        @synchronized (self) {
            if (!self.isCleaned) {
                // 手势完毕,选中的圆回归普通状态
                [self changeCircleInSelectedCircleArrayWithSate:DDYCircleViewStateNormal];
                // 清空保存选中的数组
                [self.selectedCircleArray removeAllObjects];
                // 清空方向
                [self resetAllCirclesDirection];
                // 完成后改变clean状态
                self.isCleaned = YES;
            }
        }
    }
    
    #pragma mark 清空所有子控件方向
    - (void)resetAllCirclesDirection {
        [self.subviews enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
            circle.angle = 0;
        }];
    }
    
    #pragma mark 改变选中数组子控件状态
    - (void)changeCircleInSelectedCircleArrayWithSate:(DDYCircleViewState)state {
        [self.selectedCircleArray enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
            circle.state = state;
            // 如果是错误状态,那就将最后一个按钮特殊处理
            if (state == DDYCircleViewStateError && idx == self.selectedCircleArray.count-1) {
                circle.state = DDYCircleViewStateLastOneError;
            }
        }];
        [self setNeedsDisplay];
    }
    
    #pragma mark 每添加一个圆计算一次方向,同时处理跳跃连线
    - (void)calculateAngleAndConnectJumpedCircle {
        if (self.selectedCircleArray && self.selectedCircleArray.count>1) {
            // 最后一个对象
            DDYCircleView *last1 = [self.selectedCircleArray lastObject];
            // 倒数第二个对象
            DDYCircleView *last2 = self.selectedCircleArray[self.selectedCircleArray.count-2];
            // 计算角度(反正切)
            last2.angle = atan2(last1.center.y-last2.center.y, last1.center.x-last2.center.x) + M_PI_2;
            // 跳跃连线问题
            DDYCircleView *jumpedCircle = [self selectedCircleContainPoint:[self centerPointWithPoint1:last1.center point2:last2.center]];
            if (jumpedCircle && ![self.selectedCircleArray containsObject:jumpedCircle]) {
                // 把跳跃的圆添加到已选择圆的数组(插入到倒数第二个)
                [self.selectedCircleArray insertObject:jumpedCircle atIndex:self.selectedCircleArray.count-1];
            }
        }
    }
    
    #pragma mark 提供两个点返回他们中点
    - (CGPoint)centerPointWithPoint1:(CGPoint)point1 point2:(CGPoint)point2 {
        CGFloat x1 = fmax(point1.x, point2.x);
        CGFloat x2 = fmin(point1.x, point2.x);
        CGFloat y1 = fmax(point1.y, point2.y);
        CGFloat y2 = fmin(point1.y, point2.y);
        return CGPointMake((x1+x2)/2, (y1+y2)/2);
    }
    
    #pragma mark 判断点是否被圆包含(包含返回圆否则返回nil)
    - (DDYCircleView *)selectedCircleContainPoint:(CGPoint)point {
        DDYCircleView *centerCircle = nil;
        for (DDYCircleView *circle in self.subviews) {
            if (CGRectContainsPoint(circle.frame, point)) {
                centerCircle = circle;
            }
        }
        if (![self.selectedCircleArray containsObject:centerCircle]) {
            // 跳跃的点角度和已选择的倒数第二个角度一致
            centerCircle.angle = [self.selectedCircleArray[self.selectedCircleArray.count-2] angle];
        }
        return centerCircle;
    }
    
    #pragma mark 错误回显重绘
    - (void)errorToDisplay {
        if ([self circleState] == DDYCircleViewStateError || [self circleState] == DDYCircleViewStateLastOneError) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(lockDisplayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self gestureReset];
            });
        } else {
            [self gestureReset];
        }
    }
    
    #pragma mark 获取当前圆的状态
    - (DDYCircleViewState)circleState {
        return [self.selectedCircleArray firstObject].state;
    }
    
    - (void)drawRect:(CGRect)rect {
        // 没有任何选中则return
        if (self.selectedCircleArray && self.selectedCircleArray.count) {
            UIColor *lineColor = [self circleState]==DDYCircleViewStateError ? lineErrorColor : LineNormalColor;
            
            CGContextRef ctx = UIGraphicsGetCurrentContext();
            CGContextAddRect(ctx, rect);
            // 是否裁剪
            if (self.clip) {
                [self.subviews enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
                    // 确定裁剪的形状
                    CGContextAddEllipseInRect(ctx, circle.frame);
                }];
            }
            CGContextEOClip(ctx);
            
            for (int i = 0; i<self.selectedCircleArray.count; i++) {
                DDYCircleView *circle = self.selectedCircleArray[i];
                i==0 ? CGContextMoveToPoint(ctx, circle.center.x, circle.center.y) : CGContextAddLineToPoint(ctx, circle.center.x, circle.center.y);
            }
            
            // 连接最后一个按钮到手指当前触摸点
            if (!CGPointEqualToPoint(self.currentPoint, CGPointZero)) {
                [self.subviews enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
                    if ([self circleState]==DDYCircleViewStateError || [self circleState]==DDYCircleViewStateLastOneError) {
                        // 错误状态下不连接到当前点
                    } else {
                        CGContextAddLineToPoint(ctx, self.currentPoint.x, self.currentPoint.y);
                    }
                }];
            }
            
            CGContextSetLineCap(ctx, kCGLineCapRound);
            CGContextSetLineJoin(ctx, kCGLineJoinRound);
            CGContextSetLineWidth(ctx, lockLineWidth);
            [lineColor set];
            CGContextStrokePath(ctx);
        }
    }
    
    @end
    

    最后利用手势区域生成不同功能(设置手势,验证手势,登录手势)控制器

    #import "DDYGestureLockVC.h"
    
    @interface DDYGestureLockVC ()<DDYLockViewDelegate, UINavigationControllerDelegate>
    
    /** 解锁九宫格 */
    @property (nonatomic, strong) DDYLockView *lockView;
    /** TypeSetting/TypeLogin 提示文字, TypeVerify清爽无提示 */
    @property (nonatomic, strong) UILabel *tipLabel;
    /** TypeSetting/TypeLogin 提示视图, TypeVerify清爽无提示 */
    @property (nonatomic, strong) DDYLockInfoView *infoView;
    
    @end
    
    @implementation DDYGestureLockVC
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self setupSameUI];
        [self setupDifferentUI];
    }
    
    #pragma mark UI相同部分
    - (void)setupSameUI {
        self.view.backgroundColor = lockViewBgColor;
        // 解锁九宫格
        _lockView = [DDYLockView lockViewWithType:self.lockType];
        _lockView.delegate = self;
        [self.view addSubview:_lockView];
        // 提示文字
        _tipLabel = UILabelNew.labFont(DDYFont(14)).labAlignmentCenter();
        [self.view addSubview:_tipLabel.viewSetFrame(0,0,DDYSCREENW,16).viewSetCenterY(_lockView.ddy_y-30)];
    }
    
    #pragma mark UI不同部分
    - (void)setupDifferentUI {
        switch (self.lockType) {
            case DDYLockViewTypeSetting:
                [self setupViewTypeSetting];
                break;
            case DDYLockViewTypeLogin:
                [self setupViewTypeLogin];
                break;
            case DDYLockViewTypeVerify:
                [self setupViewTypeVerify];
                break;
        }
    }
    
    #pragma mark 设置模式界面
    - (void)setupViewTypeSetting {
        self.navigationItem.title = lockTitleSetting;
        self.lockView.type = self.lockType;
        [self showNormalMsg:lockTipBeforeSet];
        _infoView = [DDYLockInfoView infoViewWithFrame:DDYRect(0, 0, 1.2*circleRadius, 1.2*circleRadius)];
        _infoView.center = DDYPoint(DDYSCREENW/2, _tipLabel.ddy_y - _infoView.ddy_h/2-10);
        [self.view addSubview:_infoView];
    }
    
    #pragma mark 登录模式界面
    - (void)setupViewTypeLogin {
        self.navigationItem.title = lockTitleLogin;
        DDYHeader *header = [DDYHeader headerWithHeaderWH:65];
        header.center = DDYPoint(DDYSCREENW/2, DDYSCREENH/5);
        header.imgArray = @[[UIImage imageWithColor:DDY_Red size:DDYSize(65, 65)]];
        header.urlArray = @[@""];
        [self.view addSubview:header];
        // 提示请输入手势密码
        [self showNormalMsg:lockTipLoginTip];
        // 忘记手势密码?
        
        // 指纹解锁
    }
    
    #pragma mark 验证模式界面
    - (void)setupViewTypeVerify {
        self.navigationItem.title = lockTitleSetting;
    }
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        // 清空第一个密码【登录验证都第二个(不关心第一个),设置从第一个开始(此时第一个为空)】
        DDYUserDefaultsSet(nil, lockOneKey);
    }
    
    #pragma mark DDYLockViewDelegate and UINavigationControllerDelegate
    - (void)lockView:(DDYLockView *)lockView state:(DDYLockViewState)state {
        switch (state) {
            case DDYLockViewStateLess:
                [self showWarningAndShake:lockTipConnectLess];
                break;
            case DDYLockViewStateFirstFinish:
            {
                [self showNormalMsg:lockTipDrawAgain];
                [self infoViewSelectedSameAsLockView:lockView];
            }
                break;
            case DDYLockViewStateSecondFinish:
            {
                [self showNormalMsg:lockTipSuccess];
                [self.navigationController popToRootViewControllerAnimated:YES];
            }
                break;
            case DDYLockViewStateSecondError:
            {
                [self showWarningAndShake:lockTipDrawAgainError];
                [self infoViewDeselectedAllCircle];
            }
                break;
            case DDYLockViewStateLoginFinish:
                [self dismissViewControllerAnimated:YES completion:^{ }];
                break;
            case DDYLockViewStateLoginError:
                [self showWarningAndShake:lockTipVerifyError];
                break;
            case DDYLockViewStateVerifyFinish:
                [self dismissViewControllerAnimated:YES completion:^{ }];
                break;
            case DDYLockViewStateVerifyError:
                [self showWarningAndShake:lockTipVerifyError];
                break;
        }
    }
    
    #pragma mark - tipLabel不同提示
    #pragma mark 普通提示
    - (void)showNormalMsg:(NSString *)msg {
        [_tipLabel setText:msg];
        [_tipLabel setTextColor:lockTipNormalColor];
    }
    
    #pragma mark 摇动警示
    - (void)showWarningAndShake:(NSString *)msg {
        CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"];
        animation.values = @[@(-5),@(0),@(5),@(0),@(-5),@(0),@(5),@(0)];
        animation.duration = 0.3f;
        animation.repeatCount = 2;
        animation.removedOnCompletion = YES;
        [_tipLabel setText:msg];
        [_tipLabel setTextColor:lockTipWarningColor];
        [_tipLabel.layer addAnimation:animation forKey:@"shake"];
    }
    
    #pragma mark - infoView操作
    #pragma mark 设置状态第一次设置后infoView展示相应选中
    - (void)infoViewSelectedSameAsLockView:(DDYLockView *)lockView {
        for (DDYCircleView *circle in lockView.subviews) {
            if (circle.state==DDYCircleViewStateSelected || circle.state==DDYCircleViewStateLastOneSelected) {
                for (DDYCircleView *infoCircle in _infoView.subviews) {
                    if (infoCircle.tag == circle.tag) {
                        infoCircle.state = DDYCircleViewStateSelected;
                    }
                }
            }
        }
    }
    
    #pragma mark 让infoView按钮全部取消选中
    - (void)infoViewDeselectedAllCircle {
        [_infoView.subviews enumerateObjectsUsingBlock:^(DDYCircleView *circle, NSUInteger idx, BOOL *stop) {
            circle.state = DDYCircleViewStateNormal;
        }];
    }
    
    @end
    
    
    MyLockView.jpg

    相关文章

      网友评论

          本文标题:九宫格手势密码

          本文链接:https://www.haomeiwen.com/subject/ogfonxtx.html