首页 > 代码库 > [iOS UI进阶 - 5.0] 手势解锁Demo

[iOS UI进阶 - 5.0] 手势解锁Demo

A.需求
1.九宫格手势解锁
2.使用了绘图和手势事件
 
code source: https://github.com/hellovoidworld/GestureUnlockDemo
 
技术分享
 
B.实现
  • 使用按钮来处理每个圆点
  • 使用代码生成按钮
  • 取消按钮点击事件
  • 设置普通状态和选中状态的背景图片
  • CGRectContainsPoint,移动到按钮范围内改变按钮为选中状态
  • 按钮的连接:使用数组存储被选中的所有按钮,画上连线
  • 已经连线的按钮不需要再连线
  • 触摸结束清空连线和按钮选中状态
  • 移动中也要画出线,最后的点用来辅助画移动中的线
  • 解决bug:每次触摸开始重置当前画笔位置
  • 设置触摸触发选中的按钮内部范围
  • 使用tag记录按钮的选中顺序轨迹,触摸结束取得轨迹
  • 封装整个手势解锁view成为一个自定义控件
  • 封装按钮称为自定类
 
1.准备基础界面,使用一个UIView作为解锁画面
技术分享
 
2.在控制器ViewController设置一下背景图片和状态栏
 1 // 2 //  ViewController.m 3 //  HVWLockView 4 // 5 //  Created by hellovoidworld on 15/1/12. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import "ViewController.h"10 11 @interface ViewController ()12 13 @end14 15 @implementation ViewController16 17 - (void)viewDidLoad {18     [super viewDidLoad];19     // Do any additional setup after loading the view, typically from a nib.20    21     // 设置背景22     self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Home_refresh_bg"]];23 }24 25 /** 设置状态栏样式 */26 - (UIStatusBarStyle)preferredStatusBarStyle {27     return UIStatusBarStyleLightContent;28 }29 30 - (void)didReceiveMemoryWarning {31     [super didReceiveMemoryWarning];32     // Dispose of any resources that can be recreated.33 }34 35 36 @end
 
3.自定义解锁画面的类HVWLockView
4.使用代码初始化HVWLockView的子控件—按钮,设置按钮的样式、位置尺寸
 1 // 2 //  HVWLockView.m 3 //  HVWLockView 4 // 5 //  Created by hellovoidworld on 15/1/12. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import "HVWLockView.h"10 #import "HVWLockButton.h"11 12 #define BUTTON_COUNT 913 #define BUTTON_COL_COUNT 314 15 @implementation HVWLockView16 17 #pragma mark - 初始化方法18 /** 使用文件初始化 */19 - (id)initWithCoder:(NSCoder *)aDecoder {20     if (self = [super initWithCoder:aDecoder]) {21         [self initView];22     }23     return self;24 }25 26 /** 使用代码初始化 */27 - (instancetype)initWithFrame:(CGRect)frame {28     if (self = [super initWithFrame:frame]) {29         [self initView];30     }31     return self;32 }33 34 /** 初始化view内的控件(按钮) */35 - (void) initView {36     for (int i=0; i<BUTTON_COUNT; i++) {37         HVWLockButton *button = [HVWLockButton buttonWithType:UIButtonTypeCustom];38        39         // 取消点击时间40         button.userInteractionEnabled = NO;41        42         // 设置指标tag,用来记录轨迹43         button.tag = i;44        45         // 设置普通状态图片46         [button setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];47        48         // 设置选中状态图片49         [button setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];50        51         // 加入按钮到lock view52         [self addSubview:button];53     }54 }55 56 /** 设置按钮位置尺寸 */57 - (void)layoutSubviews {58     [super layoutSubviews];59    60     // 取出所有按钮61     for (int i=0; i<self.subviews.count; i++) {62         HVWLockButton *button = self.subviews[i];63         CGFloat buttonWidth = 74;64         CGFloat buttonHeight = 74;65 66         // 此按钮所在列号67         int col = i % BUTTON_COL_COUNT;68         // 此按钮所在行号69         int row = i / BUTTON_COL_COUNT;70         // 等分水平多余空间,计算出间隙71         CGFloat marginX = (self.frame.size.width - BUTTON_COL_COUNT * buttonWidth) / (BUTTON_COL_COUNT + 1);72         CGFloat marginY = marginX;73        74         // x坐标75         CGFloat buttonX = marginX + col * (buttonWidth + marginX);76         // y坐标77         CGFloat buttonY = marginY + row * (buttonHeight + marginY);78        79         button.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);80     }81 }82 83 @end
 
out:
技术分享
 
 
5.实现触摸事件方法
(1)点击开始,使被点击的按钮改变为选中状态(改变图片)
(2)点击拖曳中,同样使被触碰到的按钮改变为选中状态
(3)点击结束,清空选中状态
(4)小修改:把HVWLockView背景改为透明
 
HVWLockView:
 1 #pragma mark - 触摸事件 2 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 3     UITouch *touch = [touches anyObject]; 4     CGPoint touchLocation = [touch locationInView:touch.view]; 5     6     // 检测哪个按钮被点中了 7     for (HVWLockButton *button in self.subviews) { 8         if (CGRectContainsPoint(button.frame, touchLocation)) { 9             button.selected = YES;10         }11     }12 13 }14 15 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {16     UITouch *touch = [touches anyObject];17     CGPoint touchLocation = [touch locationInView:touch.view];18    19     // 检测哪个按钮被点中了20     for (HVWLockButton *button in self.subviews) {21         if (CGRectContainsPoint(button.frame, touchLocation)) {22             button.selected = YES;23         }24     }25 }26 27 - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {28     // 消除所有按钮选中状态29     for (HVWLockButton *button in self.subviews) {30         button.selected = NO;31     }32 }

 

 
out:
技术分享
 
6.画出连接线
(1)优化:将按钮封装为一个类HVWLockButton
 1 // 2 //  HVWLockButton.m 3 //  HVWLockView 4 // 5 //  Created by hellovoidworld on 15/1/12. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import "HVWLockButton.h"10 11 @implementation HVWLockButton12 13 /** 使用文件创建会调用 */14 - (id)initWithCoder:(NSCoder *)aDecoder {15     if (self = [super initWithCoder:aDecoder]) {16         [self initLockButton];17     }18     return self;19 }20 21 /** 使用代码创建会调用 */22 - (instancetype)initWithFrame:(CGRect)frame {23     if (self = [super initWithFrame:frame]) {24         [self initLockButton];25     }26     return self;27 }28 29 /** 初始化 */30 - (void) initLockButton {31     // 取消交互事件(点击)32     self.userInteractionEnabled = NO;33    34     // 设置普通状态图片35     [self setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];36    37     // 设置选中状态图片38     [self setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];39 }40 41 @end
 
(2)使用一个数组来存储已经被选择的按钮
(3)把触碰到的按钮到加入到上述数组中
(4)在绘图方法中把数组内的按钮用线连起来
(5)使用一个成员变量来存储当前触摸位置,画出最后触摸的按钮到现触摸点的线
(6)重复触碰同一个按钮的时候,不用重绘和计算
(7)创建一个代理方法,在触摸结束的时候输出轨迹序列
(8)精简一下代码
 
  1 //  2 //  HVWLockView.m  3 //  HVWLockView  4 //  5 //  Created by hellovoidworld on 15/1/12.  6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.  7 //  8   9 #import "HVWLockView.h" 10 #import "HVWLockButton.h" 11  12 #define BUTTON_COUNT 9 13 #define BUTTON_COL_COUNT 3 14  15 @interface HVWLockView() 16  17 /** 已选择按钮数组 */ 18 @property(nonatomic, strong) NSMutableArray *selectedButtons; 19  20 /** 触摸位置 */ 21 @property(nonatomic, assign) CGPoint currentTouchLocation; 22  23 @end 24  25 @implementation HVWLockView 26  27 /** 初始化数组 */ 28 - (NSMutableArray *)selectedButtons { 29     if (nil == _selectedButtons) { 30         _selectedButtons = [NSMutableArray array]; 31     } 32     return _selectedButtons; 33 } 34  35 #pragma mark - 初始化方法 36 /** 使用文件初始化 */ 37 - (id)initWithCoder:(NSCoder *)aDecoder { 38     if (self = [super initWithCoder:aDecoder]) { 39         [self initView]; 40     } 41     return self; 42 } 43  44 /** 使用代码初始化 */ 45 - (instancetype)initWithFrame:(CGRect)frame { 46     if (self = [super initWithFrame:frame]) { 47         [self initView]; 48     } 49     return self; 50 } 51  52 /** 初始化view内的控件(按钮) */ 53 - (void) initView { 54     // 设置透明背景 55     self.backgroundColor = [[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:0]; 56     57     for (int i=0; i<BUTTON_COUNT; i++) { 58         HVWLockButton *button = [HVWLockButton buttonWithType:UIButtonTypeCustom]; 59         60         // 设置指标tag,用来记录轨迹 61         button.tag = i; 62         63         // 加入按钮到lock view 64         [self addSubview:button]; 65     } 66 } 67  68 /** 设置按钮位置尺寸 */ 69 - (void)layoutSubviews { 70     [super layoutSubviews]; 71     72     // 取出所有按钮 73     for (int i=0; i<self.subviews.count; i++) { 74         HVWLockButton *button = self.subviews[i]; 75         CGFloat buttonWidth = 74; 76         CGFloat buttonHeight = 74; 77  78         // 此按钮所在列号 79         int col = i % BUTTON_COL_COUNT; 80         // 此按钮所在行号 81         int row = i / BUTTON_COL_COUNT; 82         // 等分水平多余空间,计算出间隙 83         CGFloat marginX = (self.frame.size.width - BUTTON_COL_COUNT * buttonWidth) / (BUTTON_COL_COUNT + 1); 84         CGFloat marginY = marginX; 85         86         // x坐标 87         CGFloat buttonX = marginX + col * (buttonWidth + marginX); 88         // y坐标 89         CGFloat buttonY = marginY + row * (buttonHeight + marginY); 90         91         button.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight); 92     } 93 } 94  95 #pragma mark - 触摸事件 96 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 97     98     [self touchesMoved:touches withEvent:event]; 99    100 }101 102 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {103     UITouch *touch = [touches anyObject];104     CGPoint touchLocation = [touch locationInView:touch.view];105    106     // 检测哪个按钮被点中了107     for (HVWLockButton *button in self.subviews) {108        109         // 如果触碰到了此按钮110         if (CGRectContainsPoint(button.touchFrame, touchLocation)) {111             button.selected = YES;112            113             // 如果此按钮没有被触碰过才进行处理114             if (![self.selectedButtons containsObject:button]) {115                 // 加入到数组116                 [self.selectedButtons addObject:button];117             }118         }119        120         // 当前触摸位置121         self.currentTouchLocation = touchLocation;122     }123    124     // 重绘125     [self setNeedsDisplay];126 }127 128 - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {129     // 轨迹序列130     NSMutableString *passPath = [NSMutableString string];131    132     // 合成轨迹序列133     for (HVWLockButton *button in self.selectedButtons) {134         // 添加到轨迹序列135         [passPath appendFormat:@"%d", button.tag];136     }137 138     // 调用代理方法139     if ([self.delegate respondsToSelector:@selector(hvwLockView:didFinishedWithPath:)]) {140         [self.delegate hvwLockView:self didFinishedWithPath:passPath];141     }142    143     // 清除选中状态144     [self.selectedButtons makeObjectsPerformSelector:@selector(setSelected:) withObject:@(NO)];145    146     // 清空数组147     [self.selectedButtons removeAllObjects];148    149     // 重绘150     [self setNeedsDisplay];151 }152 153 154 #pragma mark - 绘图方法155 - (void)drawRect:(CGRect)rect {156     UIBezierPath *path = [UIBezierPath bezierPath];157    158     // 遍历已选择按钮数组159     for (int i=0; i<self.selectedButtons.count; i++) {160            HVWLockButton *button = self.selectedButtons[i];161        162         if (0 == i) {163             [path moveToPoint:button.center];164         } else {165             [path addLineToPoint:button.center];166         }167     }168    169     if (self.selectedButtons.count) {170         [path addLineToPoint:self.currentTouchLocation];171     }172    173     // 设置画笔174     [[UIColor redColor] set];175     [path setLineWidth:10];176     [path setLineCapStyle:kCGLineCapRound];177     [path setLineJoinStyle:kCGLineJoinBevel];178    179     [path stroke];180 }181 182 @end
 
out:
技术分享
 
2015-01-12 16:39:23.794 HVWLockView[10274:184387] 手势解锁的输出序列:01246
 
 
 
 
 
 

[iOS UI进阶 - 5.0] 手势解锁Demo