iOS:手势安全锁

本文介绍了如何在iOS中实现手势安全锁。通过自定义View,设置9个按钮构成3x3矩阵,监听触摸事件来匹配按钮并绘制手势路径。当手指抬起时,将所选按钮的tag值组合成手势结果,并重置状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思路:

1.自定义一个View,在View的控制器中添加9格按钮,三行三列,每个按钮设一个tag值;

2.监听down与move事件,将触控点坐标与9个按钮中心点范围进行匹配,符合则加入列表中,并缓存当前移动点的坐标;

3.取出已选中列表与当前临时移动的点,在绘制方法中绘制path路径。

4.监听up事件,从已选中列表中取出每个按钮的tag值,拼成字符串,这就是手势最终结果值,然后再清空按钮选中状态与列表,重绘。

 

实现方法:

一、在Main.storyboard中拖入一个UIIView,用于存放手势各圆点的子View。

二、新建UIView的控制器类,GestureLockView.h和GestureLockView.m,在内部实现手势绘制:

1.GestureLockView.m中,重写initWithFrame和initWithCoder,用代码方法创建三行三列的9个UIButton:

/*
 UIView的初始化方法:通过代码创建时触发
 */
- (id)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if(self){
        [self initView];
    }
    return self;
}

/*
 UIView的初始化方法:通过xib或storyboard创建时触发
 */
- (id)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if(self){
        [self initView];
    }
    return self;
}

/*
 自定义初始化方法:初始化一些参数
 */
- (void)initView{
    for(int i=0;i<9;i++){
        //1.创建item View
        UIButton *itemV = [UIButton buttonWithType:UIButtonTypeCustom];
        //设置tab,用于最后手松开时获取手势结果值,每个子View一个值,连起来就是手势设置过的值
        itemV.tag = i;
        //禁用按钮接收触控事件
        itemV.userInteractionEnabled = NO;
        //默认背景图片
        UIImage *unSelectImg = [UIImage imageNamed:@"lock_unselect"];
        //2.设置按钮默认背景
        [itemV setBackgroundImage:unSelectImg forState:UIControlStateNormal];
        //选中背景图片
        UIImage *selectedImg = [UIImage imageNamed:@"lock_selected"];
        //2.设置按钮选中背景
        [itemV setBackgroundImage:selectedImg forState:UIControlStateSelected];
        //3.将按钮加到当前View中
        [self addSubview:itemV];
    }
}

2.实现layoutSubviews方法,获取9个子UIButton,计算子View的位置与大小并布局:

/*
 布局子View位置与大小(类似onLayout)
 */
- (void)layoutSubviews{
    //调父类此方法初始化
    [super layoutSubviews];
    
    int width = 70;
    int height = 70;
    CGFloat marginTopItem = (self.frame.size.height - height*3) / 4;
    CGFloat marginLeftItem = (self.frame.size.width - width*3) / 4;
    //循环取出子View,设置每个子View大小与位置
    NSInteger count = self.subviews.count;
    for (int i=0; i<count; i++) {
        //获取每个子View
        UIButton *itemV = self.subviews[i];
        /*
         计算子View的xy坐标
         */
        CGFloat x = (i % 3 + 1) * marginLeftItem + (i % 3) * width;
        CGFloat y = ((int)(i / 3) + 1) * marginTopItem + ((int)(i / 3)) * height;
        //设置子View的frame
        itemV.frame = CGRectMake(x, y, width, width);
    }
}

3.实现touchesBegan与touchesMoved,判断当前触控的点是否在9个按钮中心坐标范围内,是则加入成员变量列表中备用,以及缓存当前移动的点坐标:

//存放选中子View列表
@property (nonatomic, strong) NSMutableArray *selectViews;
//缓存当前拖动的点
@property (nonatomic, assign) CGPoint curPoint;
/*
 懒加载,初始化列表
 */
- (NSMutableArray *)selectViews{
    if(_selectViews == nil){
        _selectViews = [NSMutableArray array];
    }
    return _selectViews;
}



/*
 自定义方法,处理触控事件
 */
- (void) processTouch:(NSSet<UITouch *> *)touches{
    //获取UITouch
    UITouch *touch = [touches anyObject];
    //根据触控的View获取点xy
    CGPoint point = [touch locationInView:touch.view];
    //缓存当前移动的点坐标
    self.curPoint = point;
    for(UIButton *itemV in self.subviews){
        //判断当前触控的点是否在子View范围内,并且未选择时执行
        if([self isContains:itemV point:point] && itemV.selected == NO){
            //设置触控到的子View为选中状态
            itemV.selected = YES;
            //将选中子View的加入列表中
            [self.selectViews addObject:itemV];
        }
    }
    //重绘
    [self setNeedsDisplay];
}

/*
 自定义方法,判断当前触控是否在每个按钮范围内,是则返回YES,否则NO
 */
- (BOOL)isContains:(UIButton *)itemV point:(CGPoint)point{
    //获取子item View的中心点范围
    CGRect itemCenterFrame = CGRectMake(itemV.center.x - 20, itemV.center.y - 20, 40, 40);
    //判断中心范围与当前触控的点是否重合
    if(CGRectContainsPoint(itemCenterFrame, point)){
        return YES;
    }
    return NO;
}

/*
 指手按下时触发
 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //执行自定义方法
    [self processTouch:touches];
}
/*
 指手移动时触发
 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //执行自定义方法
    [self processTouch:touches];
}

4.实现drawRect方法,取出已选按钮列表与临时点CGPoint,绘制path路径:

/*
 UIView的初始化方法:绘制path
 */
- (void)drawRect:(CGRect)rect {
    if(self.selectViews == nil || self.selectViews.count == 0){
        return;
    }
    
    //创建路劲path
    UIBezierPath *path = [UIBezierPath bezierPath];
    NSInteger count = self.selectViews.count;
    //循环获取所有选中的子View,根据这些View的点xy坐标,创建手势路径path
    for (int i =0; i<count; i++) {
        UIButton *itemV = self.selectViews[i];
        if(i == 0){
            //设置起点
            [path moveToPoint:itemV.center];
        } else {
            //连接其他点
            [path addLineToPoint:itemV.center];
        }
    }
    
    //绘制当前移动的点
    if(CGPointEqualToPoint(self.curPoint, CGPointZero) == NO){
        [path addLineToPoint:self.curPoint];
    }
    
    //设置path宽度
    path.lineWidth = 8;
    //设置圆角
    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineCapRound;
    //设置path颜色
    [[UIColor blueColor] set];
    //绘制path
    [path stroke];
}

5.实现touchesEnded和touchesCancelled,在手指松开时,取出选中列表,获取每个子View的tag值,拉成字符串,就是一个手势轨迹值,然后重置所有按钮为未选状态,清空已选列表,重绘界面:

/*
 指手松开时触发
 */
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    /*
     将手势轨迹拼成字符串
     */
    NSMutableString *sb = [NSMutableString string]; //可变字符串, 类似StringBuilder
    for(UIButton *itemV in self.selectViews){
        //拼接每个选中的子View的tab
        [sb appendFormat:@"%d", itemV.tag];
        //重置为未选中状态
        [itemV setSelected:NO];
    }
    //打印手势轨迹
    NSLog(@"手势轨迹为:%@", sb);
    
    //调用列表中所有View的setSelected方法,重置为未选中状态
//    [self.selectViews makeObjectsPerformSelector:@selector(setSelected:) withObject:@(NO)];
    
    //清空列表,主要为了清空绘制路径
    [self.selectViews removeAllObjects];
    //重绘
    [self setNeedsDisplay];
}
/*
 触控取消时触发
 */
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //取消时执行松开操作
    [self touchesEnded:touches withEvent:event];
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值