iOS开发之手势解锁详解

A.需求
1.九宫格手势解锁
2.使用了绘图和手势事件
code source: https://github.com/hellovoidworld/GestureUnlockDemo
Image
B.实现
  • 使用按钮来处理每个圆点
  • 使用代码生成按钮
  • 取消按钮点击事件
  • 设置普通状态和选中状态的背景图片
  • CGRectContainsPoint,移动到按钮范围内改变按钮为选中状态
  • 按钮的连接:使用数组存储被选中的所有按钮,画上连线
  • 已经连线的按钮不需要再连线
  • 触摸结束清空连线和按钮选中状态
  • 移动中也要画出线,最后的点用来辅助画移动中的线
  • 解决bug:每次触摸开始重置当前画笔位置
  • 设置触摸触发选中的按钮内部范围
  • 使用tag记录按钮的选中顺序轨迹,触摸结束取得轨迹
  • 封装整个手势解锁view成为一个自定义控件
  • 封装按钮称为自定类

    1.准备基础界面,使用一个UIView作为解锁画面
    Image(1)
    2.在控制器ViewController设置一下背景图片和状态栏
    //
     //  ViewController.m
     //  HVWLockView
     //
     //  Created by hellovoidworld on 15/1/12.
     //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
     //
     
     #import "ViewController.h"
     
     @interface ViewController ()
     
     @end
     
     @implementation ViewController
     
     - (void)viewDidLoad {
         [super viewDidLoad];
         // Do any additional setup after loading the view, typically from a nib.
        
         // 设置背景
         self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Home_refresh_bg"]];
     }
     
     /** 设置状态栏样式 */
     - (UIStatusBarStyle)preferredStatusBarStyle {
         return UIStatusBarStyleLightContent;
     }
     
     - (void)didReceiveMemoryWarning {
         [super didReceiveMemoryWarning];
         // Dispose of any resources that can be recreated.
     }
     
     
     @end

    3.自定义解锁画面的类HVWLockView
    4.使用代码初始化HVWLockView的子控件—按钮,设置按钮的样式、位置尺寸
    //
     //  HVWLockView.m
     //  HVWLockView
     //
     //  Created by hellovoidworld on 15/1/12.
     //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
     //
     
     #import "HVWLockView.h"
     #import "HVWLockButton.h"
     
     #define BUTTON_COUNT 9
     #define BUTTON_COL_COUNT 3
     
     @implementation HVWLockView
     
     #pragma mark - 初始化方法
     /** 使用文件初始化 */
     - (id)initWithCoder:(NSCoder *)aDecoder {
         if (self = [super initWithCoder:aDecoder]) {
             [self initView];
         }
         return self;
     }
     
     /** 使用代码初始化 */
     - (instancetype)initWithFrame:(CGRect)frame {
         if (self = [super initWithFrame:frame]) {
             [self initView];
         }
         return self;
     }
     
     /** 初始化view内的控件(按钮) */
     - (void) initView {
         for (int i=0; i<BUTTON_COUNT; i++) {
             HVWLockButton *button = [HVWLockButton buttonWithType:UIButtonTypeCustom];
            
             // 取消点击时间
             button.userInteractionEnabled = NO;
            
             // 设置指标tag,用来记录轨迹
             button.tag = i;
            
             // 设置普通状态图片
             [button setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
            
             // 设置选中状态图片
             [button setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
            
             // 加入按钮到lock view
             [self addSubview:button];
         }
     }
     
     /** 设置按钮位置尺寸 */
     - (void)layoutSubviews {
         [super layoutSubviews];
        
         // 取出所有按钮
         for (int i=0; i<self.subviews.count; i++) {
             HVWLockButton *button = self.subviews[i];
             CGFloat buttonWidth = 74;
             CGFloat buttonHeight = 74;
     
             // 此按钮所在列号
             int col = i % BUTTON_COL_COUNT;
             // 此按钮所在行号
             int row = i / BUTTON_COL_COUNT;
             // 等分水平多余空间,计算出间隙
             CGFloat marginX = (self.frame.size.width - BUTTON_COL_COUNT * buttonWidth) / (BUTTON_COL_COUNT + 1);
             CGFloat marginY = marginX;
            
             // x坐标
             CGFloat buttonX = marginX + col * (buttonWidth + marginX);
             // y坐标
             CGFloat buttonY = marginY + row * (buttonHeight + marginY);
            
             button.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
         }
     }
     
     @end
    out:
    Image(2)
    5.实现触摸事件方法
    (1)点击开始,使被点击的按钮改变为选中状态(改变图片)
    (2)点击拖曳中,同样使被触碰到的按钮改变为选中状态
    (3)点击结束,清空选中状态
    (4)小修改:把HVWLockView背景改为透明
    HVWLockView:
    UITouch * touch = [ touches anyObject ]; CGPoint touchLocation = [ touch locationInView : touch . view ]; // 检测哪个按钮被点中了 for ( HVWLockButton * button in self . subviews ) { if ( CGRectContainsPoint ( button . frame , touchLocation )) { button . selected = YES ; } } } - ( void ) touchesMoved :( NSSet *) touches withEvent :( UIEvent *) event { UITouch * touch = [ touches anyObject ]; CGPoint touchLocation = [ touch locationInView : touch . view ]; // 检测哪个按钮被点中了 for ( HVWLockButton * button in self . subviews ) { if ( CGRectContainsPoint ( button . frame , touchLocation )) { button . selected = YES ; } } } - ( void ) touchesEnded :( NSSet *) touches withEvent :( UIEvent *) event { // 消除所有按钮选中状态 for ( HVWLockButton * button in self . subviews ) { button . selected = NO ; } }
    out:
    Image(3)
    6.画出连接线
    (1)优化:将按钮封装为一个类HVWLockButton
    //
     //  HVWLockButton.m
     //  HVWLockView
     //
     //  Created by hellovoidworld on 15/1/12.
     //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
     //
     
     #import "HVWLockButton.h"
     
     @implementation HVWLockButton
     
     /** 使用文件创建会调用 */
     - (id)initWithCoder:(NSCoder *)aDecoder {
         if (self = [super initWithCoder:aDecoder]) {
             [self initLockButton];
         }
         return self;
     }
     
     /** 使用代码创建会调用 */
     - (instancetype)initWithFrame:(CGRect)frame {
         if (self = [super initWithFrame:frame]) {
             [self initLockButton];
         }
         return self;
     }
     
     /** 初始化 */
     - (void) initLockButton {
         // 取消交互事件(点击)
         self.userInteractionEnabled = NO;
        
         // 设置普通状态图片
         [self setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
        
         // 设置选中状态图片
         [self setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
     }
     
     @end

    (2)使用一个数组来存储已经被选择的按钮
    (3)把触碰到的按钮到加入到上述数组中
    (4)在绘图方法中把数组内的按钮用线连起来
    (5)使用一个成员变量来存储当前触摸位置,画出最后触摸的按钮到现触摸点的线
    (6)重复触碰同一个按钮的时候,不用重绘和计算
    (7)创建一个代理方法,在触摸结束的时候输出轨迹序列
    (8)精简一下代码
     #define BUTTON_COUNT 9
     #define BUTTON_COL_COUNT 3
     
     @interface HVWLockView()
     
     /** 已选择按钮数组 */
     @property(nonatomic, strong) NSMutableArray *selectedButtons;
     
     /** 触摸位置 */
     @property(nonatomic, assign) CGPoint currentTouchLocation;
     
     @end
     
     @implementation HVWLockView
     
     /** 初始化数组 */
     - (NSMutableArray *)selectedButtons {
         if (nil == _selectedButtons) {
             _selectedButtons = [NSMutableArray array];
         }
         return _selectedButtons;
     }
     
     #pragma mark - 初始化方法
     /** 使用文件初始化 */
     - (id)initWithCoder:(NSCoder *)aDecoder {
         if (self = [super initWithCoder:aDecoder]) {
             [self initView];
         }
         return self;
     }
     
     /** 使用代码初始化 */
     - (instancetype)initWithFrame:(CGRect)frame {
         if (self = [super initWithFrame:frame]) {
             [self initView];
         }
         return self;
     }
     
     /** 初始化view内的控件(按钮) */
     - (void) initView {
         // 设置透明背景
         self.backgroundColor = [[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:0];
        
         for (int i=0; i<BUTTON_COUNT; i++) {
             HVWLockButton *button = [HVWLockButton buttonWithType:UIButtonTypeCustom];
            
             // 设置指标tag,用来记录轨迹
             button.tag = i;
            
             // 加入按钮到lock view
             [self addSubview:button];
         }
     }
     
     /** 设置按钮位置尺寸 */
     - (void)layoutSubviews {
         [super layoutSubviews];
        
         // 取出所有按钮
         for (int i=0; i<self.subviews.count; i++) {
             HVWLockButton *button = self.subviews[i];
             CGFloat buttonWidth = 74;
             CGFloat buttonHeight = 74;
     
             // 此按钮所在列号
             int col = i % BUTTON_COL_COUNT;
             // 此按钮所在行号
             int row = i / BUTTON_COL_COUNT;
             // 等分水平多余空间,计算出间隙
             CGFloat marginX = (self.frame.size.width - BUTTON_COL_COUNT * buttonWidth) / (BUTTON_COL_COUNT + 1);
             CGFloat marginY = marginX;
            
             // x坐标
             CGFloat buttonX = marginX + col * (buttonWidth + marginX);
             // y坐标
             CGFloat buttonY = marginY + row * (buttonHeight + marginY);
            
             button.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
         }
     }
     
     #pragma mark - 触摸事件
     - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        
         [self touchesMoved:touches withEvent:event];
        
     }
     
     - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
         UITouch *touch = [touches anyObject];
         CGPoint touchLocation = [touch locationInView:touch.view];
        
         // 检测哪个按钮被点中了
         for (HVWLockButton *button in self.subviews) {
            
             // 如果触碰到了此按钮
             if (CGRectContainsPoint(button.touchFrame, touchLocation)) {
                 button.selected = YES;
                
                 // 如果此按钮没有被触碰过才进行处理
                 if (![self.selectedButtons containsObject:button]) {
                     // 加入到数组
                     [self.selectedButtons addObject:button];
                 }
             }
            
             // 当前触摸位置
             self.currentTouchLocation = touchLocation;
         }
        
         // 重绘
         [self setNeedsDisplay];
     }
     
     - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
         // 轨迹序列
         NSMutableString *passPath = [NSMutableString string];
        
         // 合成轨迹序列
         for (HVWLockButton *button in self.selectedButtons) {
             // 添加到轨迹序列
             [passPath appendFormat:@"%d", button.tag];
         }
     
         // 调用代理方法
         if ([self.delegate respondsToSelector:@selector(hvwLockView:didFinishedWithPath:)]) {
             [self.delegate hvwLockView:self didFinishedWithPath:passPath];
         }
        
         // 清除选中状态
         [self.selectedButtons makeObjectsPerformSelector:@selector(setSelected:) withObject:@(NO)];
        
         // 清空数组
         [self.selectedButtons removeAllObjects];
        
         // 重绘
         [self setNeedsDisplay];
     }
     
     
     #pragma mark - 绘图方法
     - (void)drawRect:(CGRect)rect {
         UIBezierPath *path = [UIBezierPath bezierPath];
        
         // 遍历已选择按钮数组
         for (int i=0; i<self.selectedButtons.count; i++) {
                HVWLockButton *button = self.selectedButtons[i];
            
             if (0 == i) {
                 [path moveToPoint:button.center];
             } else {
                 [path addLineToPoint:button.center];
             }
         }
        
         if (self.selectedButtons.count) {
             [path addLineToPoint:self.currentTouchLocation];
         }
        
         // 设置画笔
         [[UIColor redColor] set];
         [path setLineWidth:10];
         [path setLineCapStyle:kCGLineCapRound];
         [path setLineJoinStyle:kCGLineJoinBevel];
        
         [path stroke];
     }
     
     @end

    out:
    Image(4)
    2015-01-12 16:39:23.794 HVWLockView[10274:184387] 手势解锁的输出序列:01246

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值