iOS-九宫格密码解锁

作者: 麦穗0615 | 来源:发表于2016-07-10 10:16 被阅读3402次

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

    一、实现效果
    实现效果
    二、手势解锁实现过程
    分析:
    #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背景图片
    设置控制器view背景图片
    ②自定义view并与控制器中新拖入的view进行关联
    自定义view并与控制器中新拖入的view进行关联
    ③搭建UI
    控件布局
    设置触摸点,实现两个代理
    ④创建存储选中按钮的数组,并把选中按钮添加其中,画线重绘
    图4.1
    图4.2
    • 解决问题:已经被连过的按钮,不能再连线。
      解析:
      1.由于每次画线的时候,我们都会调用touchbegin和touchmove方法,如果每次选中的按钮都在你触摸的范围内,都会添加到选中按钮的数组中。这样,就会造成重复添加按钮。即第二次,触摸已经选中的按钮,同样也在你触摸的范围内,这是同样也会添加到选中按钮的数组中。为了解决这个问题,我们可以在touchbegin和touchmove的判断中加一个条件 !btn.highlighted。如代码,意思是当你第二次,重复触摸同一个按钮时,如果他在你触摸的范围内且按钮的状态不是高亮状态,即向下执行。
      if (CGRectContainsPoint(btn.frame, loc)&& !btn.highlighted)
      2.还有个问题就是,当你在连接按钮的过程中,在空白间隙停止触摸,这样,会产生多余的线。要解决这个问题,首先我们要声明一个多余线段的点CGpoint类型。其次,获取多余线段的点,多余线段的点就等于你所触摸获取的点,进行一下关联。然后,把多余的线段画出来。最后,在touchend这个方法内,也就是当触摸完毕之后,那个多余的点,就等于,选中按钮数组中最后一个按钮的中心点。在重绘一下,就OK。
      避免重复添加按钮
      多余线段的解决图1
      多余线段的解决图2
      多余线段的解决图3
    ④验证密码

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

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

      //7. 多出来的一截线
      
      #import "CZView.h"
      
      @interface CZView ()
      
      //选中按钮的数组
      /**
       *  <#Description#>
       */
      @property (nonatomic,strong) NSMutableArray <UIButton *> *selectedArray;
      
      //线条颜色的属性
      @property(nonatomic,strong)UIColor *lineColor;
      
      //接收 多余的点
      @property(nonatomic,assign)CGPoint destdationPoint;
      
      @end
      
      @implementation CZView
      
      - (UIColor *)lineColor
       {
        if (!_lineColor) {
        
        _lineColor = [UIColor whiteColor];
       }
      
        return _lineColor;
      
      }
      - (NSMutableArray<UIButton *> *)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<UITouch *> *)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<UITouch *> *)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<UITouch *> *)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值为 当前所在的行 * (按钮的宽度 + 每个按钮之间的距离)

    相关文章

      网友评论

      • c2b72fe5b7f6:xcode7.3.1 中
        self.destdationPoint = [[self.buttonSelectedArr lastObject]center];
        会报错,请问是大概会有什么问题
      • 哎呦我去叫什么呢:Demo有没有啊 :kissing_heart:
      • 梦的梦:有没有Demo可以分享的:blush::blush:
      • xxxixxxx:还没细看 ,感觉很详细!谢谢分享! :grin:
      • 苏_先生:感觉简单的思路就是九个点就是九个索引 划过索引的顺序就可以
      • 清眸如画:赞一个,浅显易懂
      • 10467f277107:第一行,从第一个点 画弧线 绕过第二个点,连接第三个点,你的连线,会连上第二个点并且把第二个点加进数组吗?
        麦穗0615:@面壁者罗辑 怎么可能绕过:sweat:
        10467f277107:@_正阳_ 我的意思是 我触摸的时候,绕过了button 2的view,连接1和3,画了弧线,你的button 2号,会不会显示高亮并且连线加入数组
        麦穗0615:@面壁者罗辑 只要是高亮状态-都会被添加到数组中
      • 14a36e959fcf:表示没懂
        麦穗0615:@14a36e959fcf 我喜欢看余华、三毛的书,,仔细看就懂了,,哈哈

      本文标题:iOS-九宫格密码解锁

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