美文网首页UI动画iOS技术点
iOS开发之手势解锁功能

iOS开发之手势解锁功能

作者: 李小南 | 来源:发表于2016-03-02 21:42 被阅读1087次

    如果这篇文章帮助到了您,希望您能点击一下喜欢或者评论,你们的支持是我前进的强大动力.谢谢!

    首先看下我们要制作功能的效果如图所示:


    手势解锁4.gif

    第一步:界面搭建

    • 在storyboard中的控制器的view中放一张与view相中大小的UIImageView并设置图片如效果图所示,然后再控制器的view中再添加一个大小合适UIView来存放9个按钮子控件.
    • 代码实现添加按钮:创建一个类继承自UIView,并将这个类和上面storyboard中添加的UIView向关联.


      Snip20160302_2.png
    • 界面是一个九宫格的布局.九宫格实现思路.(需要一点数学思想哈,看的有点模糊的最好画图)
      • 先确定有多少列 cloum = 3;
      • 计算出每列之间的距离
        • 计算为: CGFloat margin = (当前View的宽度 - 列数 * 按钮的宽度) / (总列数 + 1)
      • 每一列的X的值与它当前所在的行有关
      • 当前所在的列为:curColum = i % cloum
      • 每一行的Y的值与它当前所在的行有关.
      • 当前所在的行为:curRow = i / cloum
      • 每一个按钮的X值为, margin + 当前所在的列 * (按钮的宽度+ 每个按钮之间的间距)
      • 每一个按钮的Y值为 当前所在的行 * (按钮的宽度 + 每个按钮之间的距离)

    在创建的UIView类中实现以下代码:

    由于UIView是从storyboard中加载的,所以初始化使会调用这个方法
    -(void)awakeFromNib{
        初始化 
        [self setUP];
    }
    
    
    初始化
    - (void)setUP{
       
        for (int i = 0; i < 9;  i++) {
            添加按钮 
            UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 
            设置图片 
            [btn setImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal]; 
            设置选中状态的下图片 
            [btn setImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected]; 
            添加按钮 
            [self addSubview:btn];
        } 
    }
    
    布局子控件
    - (void)layoutSubviews{
        [super layoutSubviews];
        总列数 
        int cloumn = 3; 
        按钮高宽
        CGFloat btnWH = 74;
        每列之间的间距 
        CGFloat margin = (self.bounds.size.width - cloumn * btnWH) / (cloumn + 1); 
        当前所在的列 
        int curClounm = 0; 
        当前所在的行 
        int curRow = 0;
        CGFloat x = 0; 
        CGFloat y = 0;
        取出所有的控件 
        for (int i = 0; i < self.subviews.count; i++) { 
            计算当前所在的列 
            curClounm = i % cloumn;
            计算当前所在的行. 
            curRow = i / cloumn;
            计算Y 
            x = margin + (margin + btnWH) * curClounm;
            计算Y. 
            y = (margin +btnWH) * curRow;
            UIButton *btn = self.subviews[i];
            btn.frame = CGRectMake(x, y, btnWH, btnWH);
        } 
    }
    

    第二步:设置按钮选中的状态

    Snip20160302_7.png
    /**
     *  获取当前手指所在的点
     *
     *  @param touches touches集合
     *
     *  @return 当前手指所在的点.
     */ 
    - (CGPoint)getCurrentPoint:(NSSet *)touches{
        UITouch *touch = [touches anyObject];
        return [touch locationInView:self];
    } 
    
    /**
     *  判断一个点在不在按钮上.
     *
     *  @param point 当前点
     *
     *  @return 如果在按钮上, 返回当前按钮, 如果不在返回nil.
     */ 
    - (UIButton *)btnRectContainsPoint:(CGPoint)point{
         遍历所有的子控件
        for (UIButton *btn in self.subviews) { 
             判断手指当前点在不在按钮上.
            if (CGRectContainsPoint(btn.frame, point)) { 
                在按钮上.返回当前按钮 
                return btn;
            }
        }
        return nil;
       
    } 
    
    手指点击时让按钮成选中状态
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    
        判断当前手指在不在按钮上,如果在按钮上, 让按钮成为选中状态. 
        (我将下面1,2两个方法按功能模块单独抽取出来.)
        1.获取当前手指所在的点 
        CGPoint curP = [self getCurrentPoint:touches]; 
        2.判断当前手指所在的点在不在按钮上.
        UIButton *btn  = [self btnRectContainsPoint:curP];
        if (btn) {
            btn.selected = YES; 
        }
    }
    
    手指移动时,按钮选中,连线到当前选中的按钮
    -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ 
    
        判断当前手指在不在按钮上,如果在按钮上, 让按钮成为选中状态. 
        1.获取当前手指所在的点 
        CGPoint curP = [self getCurrentPoint:touches]; 
        2.判断当前手指所在的点在不在按钮上. 
        UIButton *btn  = [self btnRectContainsPoint:curP];
        if (btn) {
            btn.selected = YES; 
        }
    }
    

    第三步:连线

    @interface ClockView()
    
    /**
     *  选中的按钮数组.
     *  每次选中一个按钮时,都把按钮添加到数组当中.移动添加到按钮当中时做一次重绘.
     *  重绘过程中取出所有保存的按钮, 判断是不是第一个按钮, 如果是第一个按钮,那就让它成为路径的起点.
     *  如果不是第一个按钮,那就添加一根线到按钮的中心点.
     */
    @property(nonatomic,strong)NSMutableArray *selectBtn; 
    
    @end
    
    懒加载数组.
    -(NSMutableArray *)selectBtn{  
        if (_selectBtn == nil) {
            _selectBtn = [NSMutableArray array];
        }
        return _selectBtn;
    }
    
    手指点击时让按钮成选中状态
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    
        判断当前手指在不在按钮上,如果在按钮上, 让按钮成为选中状态. 
        1.获取当前手指所在的点 
        CGPoint curP = [self getCurrentPoint:touches]; 
       
        2.判断当前手指所在的点在不在按钮上.
       UIButton *btn  = [self btnRectContainsPoint:curP]; 
       if (btn && btn.selected == NO) {如果按钮已经是选中状态,就不让它再添加到数组当中 
            让按钮成为选中状态 
            btn.selected = YES; 
            把选中按钮添加到数组当中 
            [self.selectBtn addObject:btn];
        }
    } 
    
    手指移动时,按钮选中,连线到当前选中的按钮
    -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ 
        判断当前手指在不在按钮上,如果在按钮上, 让按钮成为选中状态. 
        1.获取当前手指所在的点
        CGPoint curP = [self getCurrentPoint:touches]; 
        2.判断当前手指所在的点在不在按钮上. 
        UIButton *btn  = [self btnRectContainsPoint:curP]; 
        if (btn && btn.selected == NO) {//如果按钮已经是选中状态,就不让它再添加到数组当中
            让按钮成为选中状态 
            btn.selected = YES; 
            把选中按钮添加到数组当中 
            [self.selectBtn addObject:btn]; 
        }
        每次手指移动时做一次重绘. 
        [self setNeedsDisplay]; 
    }
    
    - (void)drawRect:(CGRect)rect {
        创建路径. 
        UIBezierPath *path = [UIBezierPath bezierPath];
        取出所有保存的选中按钮连线. 
        for(int i = 0; i < self.selectBtn.count;i++){
            UIButton *btn = self.selectBtn[i];
            判断当前按钮是不是第一个,如果是第一个,把它的中心设置为路径的起点. 
            if(i == 0){
                设置起点. 
                [path moveToPoint:btn.center];
            }else{
                添加一根线到当前按钮的圆心. 
                [path addLineToPoint:btn.center];
            }
        }
       
        设置颜色 
        [[UIColor redColor] set];
        设置线宽 
        [path setLineWidth:10];
        设置线的连接样式 
        [path setLineJoinStyle:kCGLineJoinRound];
        绘制路径. 
        [path stroke];
    }
    

    第四步:最后的业务逻辑

    • 实现以上功能后虽然能实现连线的功能,但是连线只能在按钮之间,按钮与手指之间并不能实现连线.下面就来处理这个问题并实现一些收尾的工作
    @interface ClockView() 
    
    /**
     *  选中的按钮数组.
     */
    @property(nonatomic,strong)NSMutableArray *selectBtn; 
    
    /**
     *  当前手指移动的点 
      * 记录当前手指的点,数组当中所有的点都绘制完毕后, 再添加一根线到当前手指所在的点.
     */
    @property(nonatomic,assign)CGPoint curP;
    
    @end
    
    手指松开时,按钮取消选中状态,清空所有的连线.
    -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    
        1.取消所有选中的按钮,查看选中按钮的顺序(根据创建按钮时绑定的tag值)
        NSMutableString *str = [NSMutableString string];
        for (UIButton *btn in self.selectBtn) {
            [str appendFormat:@"%ld",btn.tag];
            btn.selected = NO;
        }
        2.清空所有的连线.
        [self.selectBtn removeAllObjects];
        3.重绘
        [self setNeedsDisplay];
        NSLog(@"选中按钮顺序为:%@",str);
    }
    
    - (void)drawRect:(CGRect)rect {
        如果数组当中没有元素,就不让它进行绘图.直接返回. 
        if(self.selectBtn.count <= 0) return; 
        创建路径. 
        UIBezierPath *path = [UIBezierPath bezierPath]; 
        取出所有保存的选中按钮连线. 
        for(int i = 0; i < self.selectBtn.count;i++){ 
            UIButton *btn = self.selectBtn[i]; 
           判断当前按钮是不是第一个,如果是第一个,把它的中心设置为路径的起点. 
            if(i == 0){ 
               设置起点. 
                [path moveToPoint:btn.center]; 
            }else{
               添加一根线到当前按钮的圆心. 
                [path addLineToPoint:btn.center];
            } 
        }
         连完先中的按钮后, 在选中按钮之后,添加一根线到当前手指所在的点. 
        [path addLineToPoint:self.curP]; 
         设置颜色 
        [[UIColor redColor] set]; 
           设置线宽 
        [path setLineWidth:10]; 
         设置线的连接样式 
        [path setLineJoinStyle:kCGLineJoinRound]; 
         绘制路径. 
        [path stroke]; 
    }
    

    Demo已上传

    地址:http://git.oschina.net/li_xiao_nan/overhand
    写的不好的话忘大家指出,一起进步.谢谢!

    相关文章

      网友评论

      本文标题:iOS开发之手势解锁功能

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