本文主要介绍通过手势识别实现手势解锁功能,这个方法被广泛用于手机解锁,密码验证,快捷支付等功能实现。事例效果如下所示。
首先,我们先分析功能的实现过程,首先我们需要先看大致的实现过程:
1.加载九宫格页面
2.实现按钮被点击及滑动过程中按钮状态的改变
3.实现滑动过程中的连线
4.绘制完毕后判定密码是否正确,
5.密码判定后实现跳转。
下面我们就来用代码实现上述五个过程。
1.加载九宫格界面
1.1九宫格内控件的分布 3*3 ,我们可以自定义view(包含3*3个按钮),添加到viewcontroller上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
//添加view中子控件 -( void )awakefromnib { // 创建按钮 for ( int i=0; i<9; i++) { self.linecolor=[uicolor bluecolor]; uibutton *btn=[uibutton buttonwithtype:uibuttontypecustom]; btn.userinteractionenabled=no; // 设置按钮属性 [btn setbackgroundimage:[uiimage imagenamed:@ "gesture_node_normal" ] forstate:uicontrolstatenormal]; [btn setbackgroundimage:[uiimage imagenamed:@ "gesture_node_highlighted" ] forstate:uicontrolstatehighlighted ]; [btn setbackgroundimage:[uiimage imagenamed:@ "gesture_node_error" ] forstate:uicontrolstatedisabled]; [self addsubview:btn]; } } //布局view子控件 -( void )layoutsubviews { [super layoutsubviews]; cgfloat width=74; cgfloat height=74; cgfloat margin=(self.bounds.size.width-3*width)/2; // 遍历设置9个button的frame [self.subviews enumerateobjectsusingblock:^(__kindof uiview * _nonnull obj, nsuinteger idx, bool * _nonnull stop) { // 通过tag设置按钮的索引标识 obj.tag=idx; int row=( int )idx/3; int col=idx%3; obj.frame=cgrectmake(col*(margin + width), row*(margin +height), width, height); }]; } |
1.2将定义好的view通过xib添加到viewcontroller上
首先,定义一个blockview(九宫格view)的类方法,
1
2
3
4
5
|
// 加载xib文件 +(instancetype)lockview { return [[[nsbundle mainbundle]loadnibnamed:@ "myblockview" owner:nil options:nil]lastobject]; } |
然后加载到控制器上。
1
2
3
4
5
6
|
// 设置控制器view的背景图片 self.view.backgroundcolor=[uicolor colorwithpatternimage:[uiimage imagenamed:@ "bg" ]]; myblockview *blockview=[myblockview lockview]; blockview.center=self.view.center; // 将blockview添加到viewcontroller上 [self.view addsubview:blockview]; |
2.实现按钮被点击及滑动过程中按钮状态的改变
2.1定义数组类型的成员属性,用来装被点击的按钮
1
2
3
4
5
6
7
8
9
|
@property(nonatomic,strong)nsmutablearray *btnarr; //懒加载 -(nsmutablearray *)btnarr { if (_btnarr==nil) { _btnarr=[nsmutablearray array]; } return _btnarr; } |
2.2创建路径,绘制图形
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#pragma mark----绘制图形 -( void )drawrect:(cgrect)rect { if (self.btnarr.count==0 ) { return ; } // 创建路径 uibezierpath *path=[uibezierpath bezierpath]; // 遍历所有按钮进行绘制 [self.btnarr enumerateobjectsusingblock:^(__kindof uibutton * _nonnull obj, nsuinteger idx, bool * _nonnull stop) { // 第一个按钮,中心点就是起点 if (idx==0) { [path movetopoint:obj.center]; } else { [path addlinetopoint:obj.center]; } }]; [path addlinetopoint:self.currentpoint]; // 设置路径属性 path.linewidth=10; path.linecapstyle=kcglinecapround; path.linejoinstyle=kcglinejoinround; [self.linecolor setstroke]; // 渲染 [path stroke]; } |
2.3开始触摸
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#pragma mark-----开始触摸 -( void )touchesbegan:(nsset<uitouch *> *)touches withevent:(uievent *)event { // 获取触摸对象 uitouch *touch=touches.anyobject; // 获取触摸点 cgpoint loc=[touch locationinview:self]; // 遍历按钮,判定触摸点是否在按钮上 [self.subviews enumerateobjectsusingblock:^(__kindof uibutton * _nonnull obj, nsuinteger idx, bool * _nonnull stop) { bool iscontains=cgrectcontainspoint(obj.frame, loc); // 如果在按钮上,将当前按钮保存在数组中,并改变按钮状态 if (iscontains&&obj.highlighted==no) { [self.btnarr addobject:obj]; obj.highlighted=yes; } else { obj.highlighted=no; } }]; } |
2.4滑动过程中,重绘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#pragma mark----开始滑动 -( void )touchesmoved:(nsset<uitouch *> *)touches withevent:(uievent *)event { // 获取触摸对象 uitouch *touch=touches.anyobject; // 获取触摸点 cgpoint loc=[touch locationinview:self]; self.currentpoint=loc; // 遍历按钮,如果按钮在滑动路径上,就改变按钮状态 [self.subviews enumerateobjectsusingblock:^(__kindof uibutton * _nonnull obj, nsuinteger idx, bool * _nonnull stop) { bool iscontains=cgrectcontainspoint(obj.frame, loc); if (iscontains&&obj.highlighted==no) { [self.btnarr addobject:obj]; obj.highlighted=yes; } }]; // 重绘 [self setneedsdisplay]; } |
3.实现滑动过程中的连线和4.绘制完毕后判定密码是否正确
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#pragma mark----停止滑动结束 -( void )touchesended:(nsset<uitouch *> *)touches withevent:(uievent *)event { // 定义最后一个按钮 uibutton *lastbtn=[self.btnarr lastobject]; // 将最后一个按钮中心点定义为相对滑动的当前点 self.currentpoint=lastbtn.center; // 重绘 [self setneedsdisplay]; // 判定密码 self.password=[nsmutablestring string]; [self.btnarr enumerateobjectsusingblock:^( uibutton * _nonnull obj, nsuinteger idx, bool * _nonnull stop) { [self.password appendformat:@ "%@" ,@(obj.tag)]; }]; nslog(@ "%@" ,self.password); bool isok; if ([self.delegate respondstoselector:@selector(blockview:finishedwithpassword:)]) { isok= [self.delegate blockview:self finishedwithpassword:self.password]; } if (isok) { [self.btnarr enumerateobjectsusingblock:^(uibutton* _nonnull obj, nsuinteger idx, bool * _nonnull stop) { obj.highlighted=no; }]; [self.btnarr removeallobjects]; [self setneedsdisplay]; nslog(@ "密码正确" ); } else { nslog(@ "密码错误" ); } } |
注意:我们在密码判定过程中是通过根据先前布局按钮的时候定义的按钮tag值进行字符串拼接,密码传值是通过代理实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#import <uikit/uikit.h> @ class myblockview; //声明代理 @protocol myblockviewdelegate <nsobject> @optional //代理方法 -( bool ) blockview:(myblockview *)blockview finishedwithpassword:(nsstring *)password; @end @interface myblockview : uiview +(instancetype)lockview; //设置代理成员属性 @property(nonatomic,weak)id<myblockviewdelegate>delegate; @end |
5.密码判定后实现跳转。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
else { // 关闭用户交互 self.userinteractionenabled=no; [self.btnarr enumerateobjectsusingblock:^(uibutton * _nonnull obj, nsuinteger idx, bool * _nonnull stop) { self.linecolor=[uicolor redcolor]; obj.highlighted=no; obj.enabled=no; [self setneedsdisplay]; dispatch_after(dispatch_time(dispatch_time_now, (int64_t)(1.0 * nsec_per_sec)), dispatch_get_main_queue(), ^{ // 回复按钮状态 [self.btnarr enumerateobjectsusingblock:^(uibutton * _nonnull obj, nsuinteger idx, bool * _nonnull stop) { obj.enabled=yes; }]; // 恢复线条的颜色 self.linecolor=[uicolor bluecolor]; [self.btnarr removeallobjects]; [self setneedsdisplay]; }); }]; nslog(@ "密码错误" ); } self.userinteractionenabled=yes; } |
代理判定密码并实现跳转
1
2
3
4
5
6
7
8
9
10
11
12
13
|
-( bool )blockview:(myblockview *)blockview finishedwithpassword:(nsstring *)password { if ([password isequaltostring:@ "012" ]) { uiviewcontroller *two=[uiviewcontroller new ]; two.view.backgroundcolor=[uicolor greencolor]; [self.navigationcontroller pushviewcontroller:two animated:yes]; return yes; } else { return no; } } |
最后设置控制器navigationbar属性
1
2
3
4
|
[self.navigationcontroller.navigationbar setbackgroundcolor:[uicolor redcolor]]; [ self.navigationcontroller.navigationbar settitletextattributes:@{ nsforegroundcolorattributename :[uicolor whitecolor] }]; |
以上就是本文的全部内容,希望对大家的学习有所帮助。