iOS开发之手势解锁功能

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

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


手势解锁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
写的不好的话忘大家指出,一起进步.谢谢!

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

推荐阅读更多精彩内容