本文主要介绍通过手势识别实现手势解锁功能,这个方法被广泛用于手机解锁,密码验证,快捷支付等功能实现。事例效果如下所示。

首先,我们先分析功能的实现过程,首先我们需要先看大致的实现过程:
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]
}];
|
以上就是本文的全部内容,希望对大家的学习有所帮助。








发表评论
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。