概述

高仿支付宝手势解锁, 通过手势枚举去实现手势密码相对应操作。

详细

基上篇[TouchID 指纹解锁](http://www.demodashi.com/demo/10701.html) 的技术文Demo, 增加一种解锁方式: 九宫格手势解锁.

一、主要思路

在一些涉及个人隐私的场景下,为用户的安全考虑是极其有必要的。

首先,我们先分析功能的实现过程,首先我们需要先看大致的实现过程:

  • 1.创建九宫格页面(手势密码页面)

  • 2.九宫格按钮的实现及被点击及滑动过程中按钮状态的改变, 从而创建路径,实现滑动过程中的连线,绘制图形

  • 3.创建九宫格指示器 小图

  • 4.将定义好的九宫格view 和 九宫格指示器view添加到手势密码界面 控制器

  • 5.通过手势枚举去实现手势密码相对应操作。

二、程序实现

Step1 创建九宫格界面ZLGestureLockView.

1.1九宫格内控件的分布 3x3 ,我们可以自定义view(包含3x3个按钮), 选中图片和正常默认按钮图片是可以更换的。

子视图初始化

- (void)initSubviews {
self.backgroundColor = [UIColor clearColor];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self addGestureRecognizer:pan]; // 创建九宫格 9个按钮
for (NSInteger i = 0; i < 9; i++) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.userInteractionEnabled = NO;
[btn setImage:[UIImage imageNamed:@"gesture_normal"] forState:UIControlStateNormal];
[btn setImage:[UIImage imageNamed:@"gesture_selected"] forState:UIControlStateSelected];
[self addSubview:btn];
btn.tag = i + 1;
}
}

为什么要在这个方法中布局子控件,因为只调用这个方法,就表示父控件的尺寸确定

- (void)layoutSubviews {
[super layoutSubviews]; NSUInteger count = self.subviews.count; int cols = 3;//总列数 CGFloat x = 0,y = 0,w = 0,h = 0; if (Screen_Width == 320) {
w = 50;
h = 50;
} else {
w = 58;
h = 58;
} CGFloat margin = (self.bounds.size.width - cols * w) / (cols + 1);//间距 CGFloat col = 0;
CGFloat row = 0;
for (int i = 0; i < count; i++) { col = i % cols;
row = i / cols; x = margin + (w+margin)*col; y = margin + (w+margin)*row;
if (Screen_Height == 480) { // 适配4寸屏幕
y = (w + margin) * row;
}else {
y = margin +(w + margin) * row;
} UIButton *btn = self.subviews[i];
btn.frame = CGRectMake(x, y, w, h);
}
}

1.2 定义一个显示九宫格方法

注意:我们在手势密码判定过程中是通过根据之前布局按钮的时候定义的按钮tag值进行字符串拼接,密码传值是通过代理实现的。

@protocol ZLGestureLockDelegate <NSObject>
- (void)gestureLockView:(ZLGestureLockView *)lockView drawRectFinished:(NSMutableString *)gesturePassword;
@end @interface ZLGestureLockView : UIView
@property (assign, nonatomic) id<ZLGestureLockDelegate> delegate;
@end

Step2 九宫格按钮的实现及被点击及滑动过程中按钮状态的改变, 从而创建路径,实现滑动过程中的连线,绘制图形.

2.1 定义数组类型的成员属性,用来装被点击的按钮

@property (strong, nonatomic) NSMutableArray *selectBtns;
#pragma mark - getter
- (NSMutableArray *)selectBtns {
if (!_selectBtns) {
_selectBtns = [NSMutableArray array];
}
return _selectBtns;
}

2.2 创建路径,实现滑动过程中的连线,绘制图形.

只要调用这个方法就会把之前绘制的东西清空 重新绘制

- (void)drawRect:(CGRect)rect {

    if (_selectBtns.count == 0) return;

    // 把所有选中按钮中心点连线
UIBezierPath *path = [UIBezierPath bezierPath]; if (self.userInteractionEnabled) {
[[UIColor yellowColor] set];
} else {
[[UIColor orangeColor] set];
}
for (int i = 0; i < self.selectBtns.count; i ++) {
UIButton *btn = self.selectBtns[i];
if (i == 0) {
[path moveToPoint:btn.center]; // 设置起点
} else {
[path addLineToPoint:btn.center];
}
}
[path addLineToPoint:_currentPoint]; [UIColorFromRGB(0xffc8ad) set];
path.lineWidth = 6;
path.lineJoinStyle = kCGLineCapRound;
path.lineCapStyle = kCGLineCapRound;
[path stroke];
}

2.3 开始触摸, 手势密码绘制完成后保存输入密码 且 回调

#pragma mark - action pan
- (void)pan:(UIPanGestureRecognizer *)pan {
_currentPoint = [pan locationInView:self]; [self setNeedsDisplay]; for (UIButton *button in self.subviews) {
if (CGRectContainsPoint(button.frame, _currentPoint) && button.selected == NO) { button.selected = YES;
[self.selectBtns addObject:button];
}
} [self layoutIfNeeded]; if (pan.state == UIGestureRecognizerStateEnded) { // 保存输入密码
NSMutableString *gesturePwd = @"".mutableCopy;
for (UIButton *button in self.selectBtns) {
[gesturePwd appendFormat:@"%ld",button.tag-1];
button.selected = NO;
}
[self.selectBtns removeAllObjects]; // 手势密码绘制完成后回调
if ([self.delegate respondsToSelector:@selector(gestureLockView:drawRectFinished:)]) {
[self.delegate gestureLockView:self drawRectFinished:gesturePwd];
}
}
}

Step3. 创建九宫格指示器 小图ZLGestureLockIndicator

3.1 九宫格指示器 内控件的分布也是 3x3 ,我们也可以自定义view(包含3x3个按钮)

子视图初始化

- (void)initSubviews {
// 创建9个按钮
for (int i = 0; i < 9; i++) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.userInteractionEnabled = NO;
[btn setImage:[UIImage imageNamed:@"gesture_indicator_normal"] forState:UIControlStateNormal];
[btn setImage:[UIImage imageNamed:@"gesture_indicator_selected"] forState:UIControlStateSelected];
[self addSubview:btn];
[self.btns addObject:btn];
}
}
- (void)layoutSubviews { [super layoutSubviews]; NSUInteger count = self.subviews.count; int cols = 3;//总列数 CGFloat x = 0,y = 0,w = 9,h = 9;//bounds
CGFloat margin = (self.bounds.size.width - cols * w) / (cols + 1);//间距 CGFloat col = 0;
CGFloat row = 0;
for (int i = 0; i < count; i++) { col = i%cols;
row = i/cols; x = margin + (w+margin)*col;
y = margin + (w+margin)*row; UIButton *btn = self.subviews[i];
btn.frame = CGRectMake(x, y, w, h);
}
}

3.2 定义数组类型的成员属性,用来装九宫格图内被点击的按钮(及缩小显示大九宫格被选展示)

@property (nonatomic, strong) NSMutableArray *btns;
#pragma mark - getter
- (NSMutableArray *)btns {
if (!_btns) {
_btns = [NSMutableArray array];
}
return _btns;
}

3.3 回显路径

- (void)setGesturePassword:(NSString *)gesturePassword;
#pragma mark - public
- (void)setGesturePassword:(NSString *)gesturePassword { if (gesturePassword.length == 0) {
for (UIButton *button in self.btns) {
button.selected = NO;
}
return;
} for (int i = 0; i < gesturePassword.length; i++) { NSString *s = [gesturePassword substringWithRange:NSMakeRange(i, 1)]; [self.btns[s.integerValue] setSelected:YES]; }
}

Step4 将定义好的九宫格view 和 九宫格指示器view添加到手势密码界面 控制器ZLGestureLockViewController上

4.1 加载到控制器上

 // 九宫格指示器 小图
ZLGestureLockIndicator *gestureLockIndicator = [[ZLGestureLockIndicator alloc]initWithFrame:CGRectMake((self.view.frame.size.width - 60) * 0.5, 110, 60, 60)];
[self.view addSubview:gestureLockIndicator];
self.gestureLockIndicator = gestureLockIndicator;
// 九宫格 手势密码页面
ZLGestureLockView *gestureLockView = [[ZLGestureLockView alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height - self.view.frame.size.width - 60 - btnH, self.view.frame.size.width, self.view.frame.size.width)];
gestureLockView.delegate = self;
[self.view addSubview:gestureLockView];
self.gestureLockView = gestureLockView;

4.2 创建手势密码的枚举

typedef NS_ENUM(NSInteger,ZLUnlockType) {
ZLUnlockTypeCreatePsw, // 创建手势密码
ZLUnlockTypeValidatePsw // 校验手势密码
};
+ (void)deleteGesturesPassword;//删除手势密码
+ (NSString *)gesturesPassword;//获取手势密码
- (instancetype)initWithUnlockType:(ZLUnlockType)unlockType;
// 创建的手势密码
@property (nonatomic, copy) NSString *lastGesturePsw;
@property (nonatomic) ZLUnlockType unlockType;
#pragma mark - inint
- (instancetype)initWithUnlockType:(ZLUnlockType)unlockType {
if (self = [super init]) {
_unlockType = unlockType;
}
return self;
}
#pragma mark - viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; [self setupMainUI]; self.gestureLockView.delegate = self; self.resetPswBtn.hidden = YES;
switch (_unlockType) {
case ZLUnlockTypeCreatePsw:
{
self.gestureLockIndicator.hidden = NO;
self.otherAcountBtn.hidden = self.forgetPswBtn.hidden = self.nameLabel.hidden = self.headIcon.hidden = YES;
}
break;
case ZLUnlockTypeValidatePsw:
{
self.gestureLockIndicator.hidden = YES;
self.otherAcountBtn.hidden = self.forgetPswBtn.hidden = self.nameLabel.hidden = self.headIcon.hidden = NO; }
break;
default:
break;
}
}
#pragma mark - ZLgestureLockViewDelegate
- (void)gestureLockView:(ZLGestureLockView *)lockView drawRectFinished:(NSMutableString *)gesturePassword { switch (_unlockType) {
case ZLUnlockTypeCreatePsw: // 创建手势密码
{
[self createGesturesPassword:gesturePassword];
}
break;
case ZLUnlockTypeValidatePsw: // 校验手势密码
{
[self validateGesturesPassword:gesturePassword];
}
break;
default:
break;
}
}

4.3 创建手势密码 及 保存手势密码

#pragma mark - 类方法
+ (void)deleteGesturesPassword {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:GesturesPassword];
[[NSUserDefaults standardUserDefaults] synchronize];
}
+ (void)addGesturesPassword:(NSString *)gesturesPassword {
[[NSUserDefaults standardUserDefaults] setObject:gesturesPassword forKey:GesturesPassword];
[[NSUserDefaults standardUserDefaults] synchronize];
}
+ (NSString *)gesturesPassword {
return [[NSUserDefaults standardUserDefaults] objectForKey:GesturesPassword];
}
#pragma mark - private
// 创建手势密码
- (void)createGesturesPassword:(NSMutableString *)gesturesPassword { if (self.lastGesturePsw.length == 0) { if (gesturesPassword.length < 4) {
self.statusLabel.text = @"至少连接四个点,请重新输入";
[self shakeAnimationForView:self.statusLabel];
return;
} if (self.resetPswBtn.hidden == YES) {
self.resetPswBtn.hidden = NO;
} self.lastGesturePsw = gesturesPassword;
[self.gestureLockIndicator setGesturePassword:gesturesPassword];
self.statusLabel.text = @"请再次绘制手势密码";
return;
} if ([self.lastGesturePsw isEqualToString:gesturesPassword]) { // 绘制成功 [self dismissViewControllerAnimated:YES completion:^{
// 保存手势密码
[ZLGestureLockViewController addGesturesPassword:gesturesPassword];
}]; }else {
self.statusLabel.text = @"与上一次绘制不一致,请重新绘制";
[self shakeAnimationForView:self.statusLabel];
}
}

4.4 验证手势密码 及 密码错误输入的逻辑

每个产品需求不一样,我这里是可以输入错误五次且以抖动提示,否则退出重新登录.

验证手势密码:

- (void)validateGesturesPassword:(NSMutableString *)gesturesPassword {

    static NSInteger errorCount = 5;

    if ([gesturesPassword isEqualToString:[ZLGestureLockViewController gesturesPassword]]) {

        [self dismissViewControllerAnimated:YES completion:^{
errorCount = 5;
}];
} else { if (errorCount - 1 == 0) { // 你已经输错五次了! 退出重新登陆!
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"手势密码已失效" message:@"请重新登陆" delegate:self cancelButtonTitle:nil otherButtonTitles:@"重新登陆", nil];
[alertView show];
errorCount = 5;
return;
} self.statusLabel.text = [NSString stringWithFormat:@"密码错误,还可以再输入%ld次",--errorCount];
[self shakeAnimationForView:self.statusLabel];
}
}

抖动动画:

- (void)shakeAnimationForView:(UIView *)view {

    CALayer *viewLayer = view.layer;
CGPoint position = viewLayer.position;
CGPoint left = CGPointMake(position.x - 10, position.y);
CGPoint right = CGPointMake(position.x + 10, position.y); CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[animation setFromValue:[NSValue valueWithCGPoint:left]];
[animation setToValue:[NSValue valueWithCGPoint:right]];
[animation setAutoreverses:YES]; // 平滑结束
[animation setDuration:0.08];
[animation setRepeatCount:3]; [viewLayer addAnimation:animation forKey:nil];
}

4.5 底部其他按钮事件

特别是重新绘制按钮, 切记需要把创建的手势密码置为空

#pragma mark - 按钮点击事件 Anction
// 点击其他账号登陆按钮
- (void)otherAccountLogin:(id)sender {
NSLog(@"%s",__FUNCTION__);
}
// 点击重新绘制按钮
- (void)resetGesturePassword:(id)sender {
NSLog(@"%s",__FUNCTION__); self.lastGesturePsw = nil;
self.statusLabel.text = @"请绘制手势密码";
self.resetPswBtn.hidden = YES;
[self.gestureLockIndicator setGesturePassword:@""];
}
// 点击忘记手势密码按钮
- (void)forgetGesturesPassword:(id)sender {
NSLog(@"%s",__FUNCTION__);
}

Step5 通过手势枚举去实现手势密码相对应操作

创建手势密码:

 ZLGestureLockViewController *vc = [[ZLGestureLockViewController alloc] initWithUnlockType:ZLUnlockTypeCreatePsw];
[self presentViewController:vc animated:YES completion:nil];

校验手势密码:

if ([ZLGestureLockViewController gesturesPassword].length > 0) {

     ZLGestureLockViewController *vc = [[ZLGestureLockViewController alloc] initWithUnlockType:ZLUnlockTypeValidatePsw];
[self presentViewController:vc animated:YES completion:nil];
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:@"还没有设置手势密码" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[alertView show];
}

删除手势密码:

[ZLGestureLockViewController deleteGesturesPassword];

三、压缩文件截图及运行效果

1、压缩文件截图

2、运行时的截图

四、其他补充

界面性问题可以根据自己项目需求调整即可, 具体可参考代码, 项目能够直接运行!

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

iOS-高仿支付宝手势解锁(九宫格)的更多相关文章

  1. [ios仿系列]仿支付宝手势解码

    呀~.这么快就转到ios阵营了???.android还有那么多坑呢???为此我也仅仅能啃着馒头留下屈辱的眼泪了. . 本次因为开发公司产品的android版,继而ios版也负责一部分.当中一部分就是手 ...

  2. iOS高仿app源码:纯代码打造高仿优质《内涵段子》

    iOS高仿app源码:纯代码打造高仿优质<内涵段子>收藏下来 字数1950 阅读4999 评论173 喜欢133 Github 地址 https://github.com/Charlesy ...

  3. iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码

    iOS精选源码 iOS高仿微信完整项目源码 Khala: Swift 编写的iOS/macOS 路由框架 微信左滑删除效果的实现与TableViewCell的常用样式介绍 实现阴影圆角并存,渐变色背景 ...

  4. iOS高仿微信悬浮窗、忍者小猪游戏、音乐播放器、支付宝、今日头条布局滚动效果等源码

    iOS精选源码 iOS WKWebView的使用源码 模仿apple music 小播放器的交互实现 高仿微信的悬浮小窗口 iOS仿支付宝首页效果 [swift]仿微信悬浮窗 类似于今日头条,网易新闻 ...

  5. 实例源码--IOS高仿微信打飞机游戏(完整功能)

    下载源码 技术要点: 1. IOS游戏开发基础框架 2. 高仿打飞机游戏 3. 游戏背景音频技术 4.源码详细的中文注释 ……. 详细介绍: 1. IOS游戏开发基础框架 此套源码为涉及IOS游戏开发 ...

  6. iOS仿安卓手势解锁

    界面是一个九宫格的布局.九宫格实现思路. 先确定有多少列 cloum = 3; 计算出每列之间的距离 计算为: CGFloat margin = (当前View的宽度 - 列数 * 按钮的宽度) / ...

  7. AJ学IOS(35)UI之Quartz2D仿真支付宝手势解锁_代理获得密码。

    AJ分享,必须精品 效果: 实现步骤 其实这个实现起来不难 第一步先放好主要的UI,一张背景图和一个View 第二部就是把9个button放到view中,设置好按钮的默认和选中图片. 注意:创建时候的 ...

  8. iOS 高仿:花田小憩3.0.1

    前言 断断续续的已经学习Swift一年多了, 从1.2到现在的2.2, 一直在语法之间徘徊, 学一段时间, 工作一忙, 再捡起来隔段时间又忘了.思来想去, 趁着这两个月加班不是特别多, 就决定用swi ...

  9. iOS高仿城觅应用客户端项目(开发思路和代码)

    这是一款非常完整的一个ios项目,基本实现了我们常用的一些功能了,而且界面设计个人感觉还是挺不错的,是一个不错的学习ios项目,喜欢的朋友可以参考一下吧. 项目展示,由于没有数据,所以所有的cell显 ...

随机推荐

  1. [Apache] Apache 從 2.2 換至 2.4 httpd.conf 的調整筆記 (windows 環境)

    原文地址: http://www.dotblogs.com.tw/maplenote/archive/2012/07/20/apache24_httpd_conf.aspx 整理一下 Windows ...

  2. C#编程(五)----流程控制

    流控制(控制语句) 程序的代码不是按照从上往下执行的,是按照控制语句执行的. 条件语句 C#中有两个控制语句:if语句还有switch语句 1.if语句   C#中继承了C和C++中的if语句,语法直 ...

  3. DocumentManager 在标签位置显示气泡框 z

    关于DevExpress DockManager下的DocumentManager头部标签如何显示气泡框,类似Visual studio那样显示文件的路径,如下图所示,------- 方式很简单,从工 ...

  4. 命令行编译工具NMAKE

    简介 大家已经习惯于微软提供的功能强大的IDE,已经很少考虑手动编连项目了,所谓技多不压身,有空的时候还是随我一块了解一下命令行编译. C/C++/VC++程序员或有Unix/Linux编程经验应该很 ...

  5. Qt5设置应用程序图标

    1.设置应用程序图标 简单三步走,搞定 ①创建一个图标格式的文件,可以网上在线将普通的图形格式转成.ico 格式的图标文件 http://www.faviconico.org/ 这个网站可以在线转换p ...

  6. 一款超炫的jquery图片播放插件[Cloud Carousel]

    今天给大家介绍一个jquery图片播放插件,也可以说是一款幻灯片放映插件,它叫Cloud Carousel,支持自动播放.图片预览.鼠标滚轮滚动,非常酷,下图是效果预览. 该jquery图片播放项目演 ...

  7. 几行简单代码实现DIV层上显示Tooltip效果

    最近在做一个项目,要在鼠标移到层上后显示出tip提示,网上找了半天,都很麻烦,就自己修改了一个,记录在下面 测试在IE 7.8.9及 chrome 上没问题. <HTML> <HEA ...

  8. linux主机名的修改

    导读 在一个局域网中,每台机器都有一个主机名,便于主机与主机之间的区分,因此为每台机器设置主机名,以容易记忆的方法来相互访问.比如我们在局域网中可以为根据每台机器的功用来为其命名. 查看主机名命令 [ ...

  9. MFC COM调用时出现E_OUTOFMEMORY错误

    按照<com原理与应用>第五章写的基于MFC dll的COM,COM对象不是基于Automation的,自己映射了接口,也把潘爱民的源代码看了,感觉和他的代码一样呀,为什么在客户端用CoC ...

  10. 【js】利用闭包消除回调函数启动时值已经发生变化的影响

    在以下代码中,timeFun异步执行了一个匿名函数,当输出color的值时已经由green变成red,因此输出为red. function timedFun(callback){ setTimeout ...