iOS-九宫格密码解锁

前言:看了几篇,九宫格密码解锁,看着不错,拿来学习一下。

一、实现效果
iOS-九宫格密码解锁_第1张图片
实现效果
二、手势解锁实现过程
分析:
#1.监听手指在view上的移动,首先肯定需要自定义一个view,重写touch began,touch move等方法,
  当手指移动到圈上时,让其变亮。可以通过button按钮来实现。
#2.界面搭建--【九宫格】代码的方式创建9个按钮
         1).背景图片
         2).九个按钮
            (把九个按钮作为一个整体,使用一个大的view来管理这些小的view,这些小的view就是9个button)。
         3).新建一个类,对自定义的view进行管理,这个view是从storyboard创建出来的。
          会调用aweakFrameNib方法和layoutSubviews方法,前者创造控件,后者,设置按钮frame。
         4).监听手指的移动。分析程序,应该监听手指的移动,而不是按钮的点击,当手指移动到按钮的范围内时,让按钮变亮。
           (1)重写touchesbegan...方法
               1.获取按下的点
               2.判断触摸的位置是否在按钮的范围内(使用for循环)
                提示: 一个判断点是否在指定范围内的方法——CGRectContainsPoint(,);
             (2)重新touchesmoved...方法
                 说明:当手指移动到按钮上的时候,按钮变亮,因此需要重写touchesmoved方法。
                1.获取触摸的点
                2.判断触摸的点是否在按钮的范围内。
              提示:可以把上面两个功能分别进行封装,在使用的时候直接调用即可。
#3绘制线段
  思路:获取为选中状态的按钮,并把它们存到一个数组中,重写drawRect方法,从数组中取出所有的按钮,连接所有按钮的中点。
  注意:数组中不能存空值,在存储之前需要先进行判断。
  新的问题:已经被连过的按钮,不能再连线。(在存储按钮的时候判断,如果该按钮已经被连线,那么就不再添加到数组中)。
  绘制线段
         1.获取上下文
         2.取出按钮(起点和终点)
         3.渲染

如图所示:

①设置控制器view背景图片
iOS-九宫格密码解锁_第2张图片
设置控制器view背景图片
②自定义view并与控制器中新拖入的view进行关联
iOS-九宫格密码解锁_第3张图片
自定义view并与控制器中新拖入的view进行关联
③搭建UI
iOS-九宫格密码解锁_第4张图片
控件布局

iOS-九宫格密码解锁_第5张图片
设置触摸点,实现两个代理
④创建存储选中按钮的数组,并把选中按钮添加其中,画线重绘
iOS-九宫格密码解锁_第6张图片
图4.1

iOS-九宫格密码解锁_第7张图片
图4.2
  • 解决问题:已经被连过的按钮,不能再连线。
    解析:
    1.由于每次画线的时候,我们都会调用touchbegin和touchmove方法,如果每次选中的按钮都在你触摸的范围内,都会添加到选中按钮的数组中。这样,就会造成重复添加按钮。即第二次,触摸已经选中的按钮,同样也在你触摸的范围内,这是同样也会添加到选中按钮的数组中。为了解决这个问题,我们可以在touchbegin和touchmove的判断中加一个条件 !btn.highlighted。如代码,意思是当你第二次,重复触摸同一个按钮时,如果他在你触摸的范围内且按钮的状态不是高亮状态,即向下执行。
    if (CGRectContainsPoint(btn.frame, loc)&& !btn.highlighted)
    2.还有个问题就是,当你在连接按钮的过程中,在空白间隙停止触摸,这样,会产生多余的线。要解决这个问题,首先我们要声明一个多余线段的点CGpoint类型。其次,获取多余线段的点,多余线段的点就等于你所触摸获取的点,进行一下关联。然后,把多余的线段画出来。最后,在touchend这个方法内,也就是当触摸完毕之后,那个多余的点,就等于,选中按钮数组中最后一个按钮的中心点。在重绘一下,就OK。
    iOS-九宫格密码解锁_第8张图片
    避免重复添加按钮

    iOS-九宫格密码解锁_第9张图片
    多余线段的解决图1

    iOS-九宫格密码解锁_第10张图片
    多余线段的解决图2

    iOS-九宫格密码解锁_第11张图片
    多余线段的解决图3
④验证密码

解析:对与验证密码这块,基本的思路是根据选中按钮的tag值,来验证用户设置的手势密码是否与之对等。换句话来说,我们添加在自定义view的按钮,当每个按钮被触碰时,都会变成高亮状态,被添加到高亮状态的数组中。手势密码也就相当于(0~9)的密码串排序。手势密码验证是在,触摸结束后验证的。所以我们要想验证密码,必须在touchend方法里遍历高亮数组获取按钮的tag值。并存入可变字符串数组中,与自己设置的手势密码字符串进行对比。

iOS-九宫格密码解锁_第12张图片
设置按钮的tag值

密码验证正确:按钮高亮状态消失线消失
不正确:按钮红色,线消失:按钮状态消失
iOS-九宫格密码解锁_第13张图片
密码验证1

要想线消失
iOS-九宫格密码解锁_第14张图片
高亮状态消失线消失

iOS-九宫格密码解锁_第15张图片
验证


  • 代码展示:
    //1.界面 ,九个按钮 , 设置frame
    //2. 设置按钮的高亮状态
    //3. 画线
    //4. 验证密码是否正确
    //5. 正确: 按钮高亮状态消失, 线消失
    //6.不正确: 按钮红色, 线消失: 按钮状态消失

    //7. 多出来的一截线
    
    #import "CZView.h"
    
    @interface CZView ()
    
    //选中按钮的数组
    /**
     *  <#Description#>
     */
    @property (nonatomic,strong) NSMutableArray  *selectedArray;
    
    //线条颜色的属性
    @property(nonatomic,strong)UIColor *lineColor;
    
    //接收 多余的点
    @property(nonatomic,assign)CGPoint destdationPoint;
    
    @end
    
    @implementation CZView
    
    - (UIColor *)lineColor
     {
      if (!_lineColor) {
      
      _lineColor = [UIColor whiteColor];
     }
    
      return _lineColor;
    
    }
    - (NSMutableArray *)selectedArray
      {
      if (!_selectedArray) {
      
         _selectedArray = [NSMutableArray array];
    }
    
    return _selectedArray;
    }
    
     #pragma mark - 3.0画线
    
        //画线
       - (void)drawRect:(CGRect)rect {
    
        //创建路径
        UIBezierPath *path = [UIBezierPath bezierPath];
    
                  for (NSInteger i = 0; i < self.selectedArray.count; i++) {
       //起点
         if (i == 0) {
          
          [path moveToPoint:self.selectedArray[i].center];
      }else{
          
          [path addLineToPoint:self.selectedArray[i].center];
        }
      
      //终点
        }
    
      //画多出来的线
       if (self.selectedArray.count > 0) {
      
        [path addLineToPoint:self.destdationPoint];
      
      }
    
     //设置颜色
      [self.lineColor set];
    
       //渲染
       [path stroke];
    
     }
    
     #pragma mark - 2.0 设置按钮的高亮
      //1. 触摸的位置
      //2. 触摸的按钮 高亮
      //3. 判断你触摸的点 是否在按钮的范围内: 如果是存在的 设置高亮
    
       - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
        {
            //1. 触摸的位置
             UITouch *touch = touches.anyObject;
             CGPoint loc = [touch locationInView:touch.view];
             self.destdationPoint = loc;
    
        //2. 触摸的按钮 高亮
        //3. 判断你触摸的点 是否在按钮的范围内: 如果是存在的 设置高亮
          for (NSInteger i = 0; i < self.subviews.count; i++) {
      
          UIButton *btn = self.subviews[i];
           //&& !btn.highlighted 避免重复添加 已经高亮的按钮
             if (CGRectContainsPoint(btn.frame, loc) && !btn.highlighted) {//如果是存在的
          
               //设置高亮
                btn.highlighted = YES;
          
          //添加到选中按钮中
          [self.selectedArray addObject:btn];
          
           }
           }
    
           //重绘
          [self setNeedsDisplay];
          }
    
        - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
     {
         //1. 触摸的位置
         UITouch *touch = touches.anyObject;
         CGPoint loc = [touch locationInView:touch.view];
    
          //接收 多出来的点
           self.destdationPoint = loc;
    
         //2. 触摸的按钮 高亮
          //3. 判断你触摸的点 是否在按钮的范围内: 如果是存在的 设置高亮
           for (NSInteger i = 0; i < self.subviews.count; i++) {
      
         UIButton *btn = self.subviews[i];
         if (CGRectContainsPoint(btn.frame, loc)&& !btn.highlighted) {//如果是存在的
          
          //设置高亮
          btn.highlighted = YES;
          
          //添加到选中按钮中
          [self.selectedArray addObject:btn];
          
      }
      }
    
        //重绘
        [self setNeedsDisplay];
      }
    
        #pragma mark - 4.0 验证密码
    
         - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
       {
    
         //设置 多出来的点 在 手指抬起的时候为 选中按钮集合的最后一个
          self.destdationPoint = [[self.selectedArray lastObject] center];
    
        //1.获取密码
        NSMutableString *pwd = [NSMutableString string];
         for(UIButton *btn in self.selectedArray){
    
         //拼接密码
          [pwd appendFormat:@"%@",@(btn.tag)];
      
        }
    
       //2. 验证
    
      if([pwd isEqualToString:@"012"]){//正确
     // 正确: 按钮高亮状态消失, 线消失
    
        [self clearPath];
      
    }else{
      
        //不正确: 按钮红色, 线消失: 按钮状态消失
    
         for (UIButton *btn in self.selectedArray) {
          
            //按钮的状态 不能同时存在
            btn.highlighted = NO;
            btn.selected = YES;
         
        }
      
      //设置 线条颜色 为红色
      self.lineColor = [UIColor redColor];
      
      //重绘
      [self setNeedsDisplay];
      
        //关闭 交互
      self.userInteractionEnabled = NO;
      
      //延迟
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          
          [self clearPath];
           
         //开启交互
          self.userInteractionEnabled = YES;
          
      });
      }
    
      }
    
     //清空画线集合
     - (void)clearPath
     {
    
     //将原先的红色 在设置为白色
     self.lineColor = [UIColor whiteColor];
    
     //取消按钮的高亮
     for(UIButton *btn in self.selectedArray){
      
      btn.highlighted = NO;
      btn.selected = NO;
      
     }
    
     //清空 画线的集合
     [self.selectedArray removeAllObjects];
    
     //重绘
     [self setNeedsDisplay];
    
     }
    
      #pragma mark - 1.0添加9个按钮
    
      //1. aweakformnib
     //2. 懒加载
     //创建9个按钮
     - (void)awakeFromNib
     {
    
     for(NSInteger i = 0;i < 9;i++){
    
         //创建按钮
         UIButton *btn = [[UIButton alloc]init];
      
         //设置tag 是为了验证密码
         btn.tag = i;
      
         //关闭按钮的交互 是为了 触摸事件
         btn.userInteractionEnabled = NO;
      
      //设置背景图片
      [btn setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
      
      //设置按钮的高亮图片
      [btn setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateHighlighted];
      
      //设置按钮的选中状态
      [btn setBackgroundImage:[UIImage imageNamed:@"gesture_node_error"] forState:UIControlStateSelected];
      
      //添加
      [self addSubview:btn];
     }
    
     }
    
      //设置按钮的frame
    - (void)layoutSubviews
     {
     [super layoutSubviews];
    
     for (NSInteger i = 0; i < self.subviews.count; i++) {
      
      //九宫格布局
      CGFloat W = self.bounds.size.width;
      CGFloat H = self.bounds.size.height;
      
      CGFloat btnW = 74;
      CGFloat btnH = 74;
      //计算间隔
      //列数
      NSInteger columns = 3;
                      // 总宽度 - 3个按钮的宽度 / 2
      CGFloat margW = (W - columns * btnW)/(columns - 1);
      CGFloat margH = margW;
      
      //列的索引
      NSInteger col = i % columns;
      //行的索引
      NSInteger row = i / columns;
     
      CGFloat btnX = col * (margW + btnW);
      CGFloat btnY = row * (margH + btnH);
      
      //设置按钮的frame
      UIButton *btn = self.subviews[i];
      
      btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
    
     }}
    
  • 知识点补充

1.九宫格实现原理
界面是一个九宫格的布局.九宫格实现思路.
1.先确定有多少列 cloum = 3;
2.计算出每列之间的距离
2.1计算为: CGFloat margin = (当前View的宽度 - 列数 * 按钮的宽度) / (总列数 - 1)
3.每一列的X的值与它当前所在的列有关
3.1列号:curColum = i % cloum
4.每一行的Y的值与它当前所在的行有关
4.1行号:curRow = i / cloum
5.每一个按钮的X值为, margin + 当前所在的列 * (按钮的宽度+ 每个按钮之间的间距)
6.每一个按钮的Y值为 当前所在的行 * (按钮的宽度 + 每个按钮之间的距离)

你可能感兴趣的:(iOS-九宫格密码解锁)