iOS-九宫格密码解锁

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

一、实现效果
实现效果
二、手势解锁实现过程
分析:
#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值为 当前所在的行 * (按钮的宽度 + 每个按钮之间的距离)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容