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 @end
14
15 @implementation ViewController
16
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 9
13 #define BUTTON_COL_COUNT 3
14
15 @implementation HVWLockView
16
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 view
52 [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 HVWLockButton
12
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
来源:https://www.cnblogs.com/hellovoidworld/p/4218754.html




