01-手势解锁

2018-11-05  本文已影响10人  小胖子2号
1.jpeg

分析界面

  1. 当手指在上面移动时,当移动到一个按钮范围内当中, 它会把按钮给成为选中的状态.

2.并且把第一个选中的按钮当做一个线的起点,当手指移动到某个按钮上时,就会添加一根线到选中的那妞上.

3.当手指松开时,所有按钮取消选中.所有的线都清空.

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

实现步骤:
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. 在绘图方法当中.
    创建路径 
    遍历出有的选中按钮.如果是第一个按钮,把第一个按钮的中心点当做是路径的起点.
    其它按钮都直接添加一条线,到该按钮的中心.
    
    遍历完所有的选中按钮后.
    最后添加一条线到当前手指所在的点.

具体代码如下:

#import "ClockView.h"

@interface ClockView()

/**
 *  选中的按钮数组.
 */
@property(nonatomic,strong)NSMutableArray *selectBtn;

/**
 *  当前手指移动的点
 */
@property(nonatomic,assign)CGPoint curP;
@end


@implementation ClockView

//懒加载数组.
-(NSMutableArray *)selectBtn{
    
    if (_selectBtn == nil) {
        _selectBtn = [NSMutableArray array];
    }
    return _selectBtn;
}

-(void)awakeFromNib{
    //初始化
    [self setUP];
}

-(instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]) {
         //初始化
        [self setUP];
    }
    return self;
}

//初始化
- (void)setUP{
    
    for (int i = 0; i < 9;  i++) {
        //添加按钮
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        //添加按钮时设置一个Tag以便记录每一个选中的按钮
        btn.tag = i;
        
        //让按钮不能够接受事件,原因是当前按钮会拦截事件.
        btn.userInteractionEnabled = NO;
        
        //设置图片
        [btn setImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
        
        //设置选中状态的下图片
        [btn setImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
        [self addSubview:btn];
    }  
}

/**
 *  获取当前手指所在的点
 *
 *  @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.获取当前手指所在的点
    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];
    //记录当前手指移动的点.
    self.curP = curP;
}

//手指松开时,按钮取消选中状态,清空所有的连线.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{

    if (self.selectBtn.count) {
        
        //1.取消所有选中的按钮,查看选中按钮的顺序
        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];
        
        //查看是否是第一次设置密码
        NSString *keyPwd = [[NSUserDefaults standardUserDefaults] objectForKey:@"keyPwd"];
        if (!keyPwd) {
         [[NSUserDefaults standardUserDefaults] setObject:str forKey:@"keyPwd"];
         [[NSUserDefaults standardUserDefaults] synchronize];
             NSLog(@"第一次输入密码");
        } else {
            
            if ([keyPwd isEqualToString:str]) {
                NSLog(@"密码正确");
                UIAlertView *alertV = [[UIAlertView alloc] initWithTitle:@"手势输入正确" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
                [alertV show];
                      
            } else {
                NSLog(@"密码错误");
                  UIAlertView *alertV = [[UIAlertView alloc] initWithTitle:@"手势输入错误" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
                [alertV show];
            }
        }
        NSLog(@"选中按钮顺序为:%@",str);
    }
}

//布局子控件
- (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;
        x = margin + (margin + btnWH) * curClounm;
        y = (margin +btnWH) * curRow;
        UIButton *btn = self.subviews[i];
        //设置按钮的尺寸位置
        btn.frame = CGRectMake(x, y, btnWH, btnWH);
    }
}

- (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];
}

@end
上一篇下一篇

猜你喜欢

热点阅读