美文网首页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指纹解锁和手势解锁

    iOS指纹解锁和手势解锁 iOS指纹解锁和手势解锁

  • iOS开发之手势解锁功能

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

  • ios手势解锁实践

    在工作之余学习了ios的手势解锁功能,这个功能现在被广泛用于手机解锁,密码验证,快捷支付等。以下是具体实现,写得不...

  • DrawRect绘图实现手势密码控件

    公司项目中除了之前的指纹解锁外,还有手势解锁,这就扯到了手势解锁的功能实现 其实核心就是利用touchBegin,...

  • iOS开发中六种手势UITapGestureRecognizer

    iOS开发中六种手势UITapGestureRecognizer iOS开发中手势分别有六种: 轻击手势(TapG...

  • iOS 手势解锁

    定义枚举 项目中用到的手势解锁,网上搜到了一位大神所封装的,用起来很方便 KMNineBoxView是继承UIVi...

  • iOS手势解锁

    ------------- 基本思路 -------------- 搭建界面,九宫格算法 处理按钮选中状态 按钮之...

  • iOS 手势滑动解锁功能简析

    题记 在平常的生活中,我们大概经常遇见手势滑动解锁---也就是九宫格啊,已经出现好久了,虽然随着Apple的指纹解...

  • IOS学习(7)-UIGestureRecognizer

    iOS开发之手势gesture详解

  • 手工创建UIWindow

    手工创建UIWindow,可以在应用开发中将某些界面覆盖到最上层 /* eg.支付宝客户端的手势解锁功能\ 密码保...

网友评论

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

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