iOS 一一 手势解锁

源码下载地址: https://github.com/coderZYGui/ZYGesturesUnlock

效果图:


实现思路:

分析界面,当手指在上面移动时,当移动到一个按钮范围内当中, 它会把按钮给成为选中的状态.
并且把第一个选中的按钮当做一个线的起点,当手指移动到某个按钮上时,就会添加一根线到选中的那妞上.
当手指松开时,所有按钮取消选中.所有的线都清空.

实现思路:
	先判断点前手指在不在当前的按钮上.如果在按钮上,就把当前按钮成为选中状态.
	并且把当前选中的按钮添加到一个数组当中.如果当前按钮已经是选中状态,就不需要再添加到数组中了.
	每次移动时,都让它进行重绘.
	在绘图当中,遍历出所有的选中的按钮,
	判断数组当中的第一个无素,如果是第一个,那么就把它设为路径的起点.其它都在添加一根线到按钮的圆心.
	如果当前点不在按钮上.那么就记录住当前手指所在的点.直接从起点添加一根线到当前手指所在的点.
	

实现步骤:
1.搭建界面
    界面是一个九宫格的布局.九宫格实现思路.
	先确定有多少列  cloum = 3;
	计算出每列之间的距离
	计算为: CGFloat margin = (当前View的宽度 - 列数 * 按钮的宽度) / 总列数 + 1
	每一列的X的值与它当前所在的行有关
	当前所在的列为:curColum = i % cloum
	每一行的Y的值与它当前所在的行有关.
	当前所在的行为:curRow = i / cloum
	
	每一个按钮的X值为, margin + 当前所在的列 * (按钮的宽度+ 每个按钮之间的间距)
	每一个按钮的Y值为 当前所在的行 * (按钮的宽度 + 每个按钮之间的距离)
	
	具体代码为:
	总列娄
	int colum = 3;
	每个按钮的宽高
	CGFloat btnWH = 74;
	每个按钮之间的距离
	CGFloat margin = (self.bounds.size.width - colum * btnWH) / (colum + 1);
    for(int i = 0; i < self.subviews.count; i++ ){
		当前所在的列
        int curColum = i % colum;
        当前所在的行
        int curRow = i / colum;
        CGFloat x = margin + (btnWH + margin) * curColum;
        CGFloat y = (btnWH + margin) * curRow;
        取出所有的子控件
        UIButton *btn = self.subviews[i];
        btn.frame = CGRectMake(x, y, btnWH, btnWH);
    }
    
 2.监听手指在上面的点击,移动,松开都需要做操作.
 	
 	2.1在手指开始点击屏幕时,如果当前手指所在的点在按钮上, 那就让按钮成为选中状态.
		所以要遍历出所有的按钮,判断当前手指所在的点在不在按钮上,
		如何判断当前点在不在按钮上?
		当前方法就是判断一个点在不在某一个区域,如果在的话会返回Yes,不在的话,返回NO.
		CGRectContainsPoint(btn.frame, point)
 	
		在手指点击屏幕的时候,要做的事分别有
		1.获取当前手指所在的点.
			UITouch *touch = [touches anyObject];
			CGPoint curP =  [touch locationInView:self];
		2.判断当前点在不在按钮上.
			 for (UIButton *btn in self.subviews) {
    			if (CGRectContainsPoint(btn.frame, point)) {
      				  return btn;
    			}
		     }
   		3.如果当前点在按钮上,并且当前按钮不是选中的状态.
   		  那么把当前的按钮成为选中状态.
   		  并且把当前的按钮添加到数组当中.

   	
   	2.2 当手指在移动的时也需要判断.
		  判断当前点在按钮上,并且当前按钮不是选中的状态.
   		  那么把当前的按钮成为选中状态.
   		  并且把当前的按钮添加到数组当中.
		 在移动的时候做重绘的工作.
		 
    2.3 当手指离开屏幕时.
        取出所有的选中按钮,把所有选中按钮取消选中状态.
        清空选中按钮的数组.
        绘重绘的工作.
        
        
 3. 在绘图方法当中.
 	创建路径 
 	遍历出有的选中按钮.如果是第一个按钮,把第一个按钮的中心点当做是路径的起点.
 	其它按钮都直接添加一条线,到该按钮的中心.
 	
 	遍历完所有的选中按钮后.
 	最后添加一条线到当前手指所在的点.

代码如下:

storyboard文件



ZYLockView文件

//
//  ZYClockView.m
//  ZYGesturesUnlock
//
//  Created by 朝阳 on 2017/11/11.
//  Copyright © 2017年 sunny. All rights reserved.
//

#import "ZYClockView.h"
#import "ZYViewController.h"

@interface ZYClockView()

/** 当前选中的按钮数组 */
@property (nonatomic, strong) NSMutableArray *selectBtnArray;

@property (nonatomic, assign) CGPoint curPoint;

@property (nonatomic, strong) ZYViewController *ZYVC;

@end

@implementation ZYClockView

- (ZYViewController *)ZYVC
{
    if (!_ZYVC) {
        
        _ZYVC = [[ZYViewController alloc] init];
    }
    return _ZYVC;
}

- (NSMutableArray *)selectBtnArray
{
    if (!_selectBtnArray) {
        _selectBtnArray = [NSMutableArray array];
    }
    return _selectBtnArray;
}

/**
 当从xib或storyboard中加载完后调用
 */
- (void)awakeFromNib
{
    [super awakeFromNib];
    
    self.backgroundColor = [UIColor clearColor];
    
    [self setUp];
    
    // 沙盒位置
    NSLog(@"%@",NSHomeDirectory());
    
}

/**
 初始化ZYClockView
 */
- (void)setUp
{
    // 创建Button
    for (int i = 0; i < 9; i++) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        
        // 因为在下面TouchesBegin:方法中,当点击btn的时候,btn来处理事件,因为这个类是lockView类\
        TouchesBegin:方法只有在这个View内才可以响应.当让这个View上的子控件btn来处理事件时,\
        TouchesBegin: 方法不能响应. 所以应该禁止btn处理事件
        button.userInteractionEnabled = NO;
        
        button.tag = i;
        
        [button setImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
        
        [button setImage:[UIImage imageNamed:@"gesture_node_selected"] forState:UIControlStateSelected];
        
        [self addSubview:button];
        
    }
}

/**
 布局子控件
 */
- (void)layoutSubviews
{
    [super layoutSubviews];
    
    // 取出每个button,设置其frame
    
    CGFloat x = 0;
    CGFloat y = 0;
    CGFloat btnWH = 74;
    
    // 九宫格
    // 总共列
    int column = 3;
    // button之间的间距
    CGFloat margin = (self.bounds.size.width - (btnWH * column)) / (column + 1);
    // 记录当前行号 和 列号
    int curRow  = 0;
    int curCol = 0;
    
    for (int i = 0; i < self.subviews.count; ++i) {
        
        curRow = i / column;
        curCol = i % column;
        
        x = margin + (btnWH + margin) * curCol;
        y = margin + (btnWH + margin) * curRow;
        
        // 取出每一个button
        UIButton *button = self.subviews[i];
        button.frame = CGRectMake(x, y, btnWH, btnWH);
        
    }
}

/**
 获取当前手指的点
 */
- (CGPoint)getCurrentPoint:(NSSet *)touches
{
    UITouch *touch = [touches anyObject];
    return [touch locationInView:self];
}

// 给定一个点,判断点是否在按钮身上
- (UIButton *)btnRectContainsPoint:(CGPoint)point
{
    // 取出所有的按钮
    for (UIButton *button in self.subviews) {
        if (CGRectContainsPoint(button.frame, point)) {
            return button;
        }
    }
    return nil;
}

/**
 开始点击的时候调用
 
 @param touches 不规则的UITouch集合
 @param event 触发的事件
 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //1. 获取当前手指所在的点
    //    UITouch *touch = [touches anyObject];
    //    CGPoint curPoint = [touch locationInView:self];
    
    CGPoint curPoint = [self getCurrentPoint:touches];
    
    //2. 取出所有的按钮
    //    for (UIButton *button in self.subviews) {
    //        // 判断当前手指所在的点,是否在按钮范围内
    //        // CGRectContainsRect方法: 某一个点是否在某个范围内
    //        //  arg1: 某一范围   arg2: 某一点
    //        if (CGRectContainsPoint(button.frame, curPoint)) {
    //            button.selected = YES;
    //        }
    //    }
    
    // 判断当前手指是否在按钮的frame内,如果在则设置按钮为选中状态
    UIButton *button = [self btnRectContainsPoint:curPoint];
    if (button && button.selected == NO) {
        button.selected = YES;
        // 保存当前选中的按钮
        [self.selectBtnArray addObject:button];
    }
}

/**
 开始移动的时候调用
 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //1. 获取当前手指所在的点
    CGPoint curPoint = [self getCurrentPoint:touches];
    // 记录当前手指所在的点
    self.curPoint = curPoint;
    
    //2. 取出所有的按钮
    UIButton *button = [self btnRectContainsPoint:curPoint];
    // 只是当前点在button上 和 button不是选中状态下
    if (button && button.selected == NO) {
        button.selected = YES;
        // 保存当前选中的按钮
        [self.selectBtnArray addObject:button];
    }
    // 重绘
    [self setNeedsDisplay];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSMutableString *string = [NSMutableString string];
    //1. 取消选中按钮的选中状态
    for (UIButton *button in self.selectBtnArray) {
//        NSLog(@"%ld",button.tag);
        button.selected = NO;
        // 拼接button的索引
        [string appendFormat:@"%ld",button.tag];
    }
    
    //2. 移除绘制路径
    [self.selectBtnArray removeAllObjects];
    [self setNeedsDisplay];
    
    // 查看是否是第一次设置密码
    NSString *keyPwd = [[NSUserDefaults standardUserDefaults] objectForKey:@"keyPwd"];
    if (!keyPwd) {
        // 使用偏好设置保存用户的信息到沙盒Library/Preferences目录中
        [[NSUserDefaults standardUserDefaults] setObject:string forKey:@"keyPwd"];
        [[NSUserDefaults standardUserDefaults] synchronize];
        
    }else{
        if ([keyPwd isEqualToString:string]) {
            NSLog(@"密码正确");
//            UIAlertView *alertV =[[UIAlertView alloc] initWithTitle:@"手势输入正确" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
//            [alertV show];
            
            // 主窗口的根控制器
            UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
            [rootVC presentViewController:self.ZYVC animated:YES completion:nil];
            
        }else{
            NSLog(@"密码错误");
            UIAlertView *alertV =[[UIAlertView alloc] initWithTitle:@"手势输入错误,请重试" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
            [alertV show];
            
        }
    }
    
    //3. 查看当前选中按钮的顺序
    NSLog(@"选中按钮的顺序为:%@",string);
}

/**
 绘制方法
 */
- (void)drawRect:(CGRect)rect
{
    // 当程序一进来就调用drawRect方法,此时self.curPoint还没有值,选中按钮数组中没有值.
    if (self.selectBtnArray.count) {
        //1. 创建路径
        UIBezierPath *path = [UIBezierPath bezierPath];
        //2. 取出所有选中按钮
        for (int i = 0; i < self.selectBtnArray.count; ++i) {
            // 取出选中的button
            UIButton *selectBtn = self.selectBtnArray[i];
            // 如果是第一个按钮,就设置为起点
            if (i == 0) {
                [path moveToPoint:selectBtn.center];
            }else{
                // 连接一根线到btn的中心
                [path addLineToPoint:selectBtn.center];
            }
        }
        
        // 在当前手指所在的点上添加一根线
        [path addLineToPoint:self.curPoint];
        
        // 设置连线的状态
        [path setLineWidth:5];
        [[UIColor blueColor] set];
        [path setLineJoinStyle:kCGLineJoinRound];
        
        //3. 绘制
        [path stroke];
    }
}

@end


ZYLabelView文件
#import "ZYLabelView.h"

@implementation ZYLabelView

- (void)drawRect:(CGRect)rect {
    
    NSString *str = @"请输入手势密码";
    NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
    dictM[NSFontAttributeName] = [UIFont systemFontOfSize:20];
    dictM[NSForegroundColorAttributeName] = [UIColor lightGrayColor];
    
    [str drawAtPoint:CGPointZero withAttributes:dictM];
}

@end


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

white camel

感谢支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值