iOS-高仿支付宝手势解锁(九宫格)
概述
详细
基上篇[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-高仿支付宝手势解锁(九宫格)的更多相关文章
- [ios仿系列]仿支付宝手势解码
呀~.这么快就转到ios阵营了???.android还有那么多坑呢???为此我也仅仅能啃着馒头留下屈辱的眼泪了. . 本次因为开发公司产品的android版,继而ios版也负责一部分.当中一部分就是手 ...
- iOS高仿app源码:纯代码打造高仿优质《内涵段子》
iOS高仿app源码:纯代码打造高仿优质<内涵段子>收藏下来 字数1950 阅读4999 评论173 喜欢133 Github 地址 https://github.com/Charlesy ...
- iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码
iOS精选源码 iOS高仿微信完整项目源码 Khala: Swift 编写的iOS/macOS 路由框架 微信左滑删除效果的实现与TableViewCell的常用样式介绍 实现阴影圆角并存,渐变色背景 ...
- iOS高仿微信悬浮窗、忍者小猪游戏、音乐播放器、支付宝、今日头条布局滚动效果等源码
iOS精选源码 iOS WKWebView的使用源码 模仿apple music 小播放器的交互实现 高仿微信的悬浮小窗口 iOS仿支付宝首页效果 [swift]仿微信悬浮窗 类似于今日头条,网易新闻 ...
- 实例源码--IOS高仿微信打飞机游戏(完整功能)
下载源码 技术要点: 1. IOS游戏开发基础框架 2. 高仿打飞机游戏 3. 游戏背景音频技术 4.源码详细的中文注释 ……. 详细介绍: 1. IOS游戏开发基础框架 此套源码为涉及IOS游戏开发 ...
- iOS仿安卓手势解锁
界面是一个九宫格的布局.九宫格实现思路. 先确定有多少列 cloum = 3; 计算出每列之间的距离 计算为: CGFloat margin = (当前View的宽度 - 列数 * 按钮的宽度) / ...
- AJ学IOS(35)UI之Quartz2D仿真支付宝手势解锁_代理获得密码。
AJ分享,必须精品 效果: 实现步骤 其实这个实现起来不难 第一步先放好主要的UI,一张背景图和一个View 第二部就是把9个button放到view中,设置好按钮的默认和选中图片. 注意:创建时候的 ...
- iOS 高仿:花田小憩3.0.1
前言 断断续续的已经学习Swift一年多了, 从1.2到现在的2.2, 一直在语法之间徘徊, 学一段时间, 工作一忙, 再捡起来隔段时间又忘了.思来想去, 趁着这两个月加班不是特别多, 就决定用swi ...
- iOS高仿城觅应用客户端项目(开发思路和代码)
这是一款非常完整的一个ios项目,基本实现了我们常用的一些功能了,而且界面设计个人感觉还是挺不错的,是一个不错的学习ios项目,喜欢的朋友可以参考一下吧. 项目展示,由于没有数据,所以所有的cell显 ...
随机推荐
- 做iphone5适配时,一定要做好图片大小的调整。
- 【微信小程序】处理时间格式,时间戳转化展示时间格式问题,调用外部js的默认方法function的问题
默认的 小程序中new Date()显示的时间是这样的: 格式化时间的显示怎么做: 小程序的根目录下util目录下默认有一个util.js文件 其中util.js文件内容是: //数据转化 funct ...
- 解决sdk manager下载非常慢或者下载失败
有了sdk manager,打开它,想下载一些须要的东西总是会发现非常慢,然后就仅仅好慢慢等待,等待许久之后最后是失败了,这样就会非常麻烦,以下我总结总结,怎样解决这些问题,让你在分分钟下载好这些东西 ...
- 应用Flume+HBase采集和存储日志数据
1. 在本方案中,我们要将数据存储到HBase中,所以使用flume中提供的hbase sink,同时,为了清洗转换日志数据,我们实现自己的AsyncHbaseEventSerializer. pac ...
- 某浪PHP面试题及答案优化
前几天一网友去国内某知名IT公司某浪面试PHP,在他谈心得的时候得一面试题,下面写问题答案及优化方案,希望大家多提提意见. “神马,杨辉三角?” 我估计太久不上学了,当年初中数学及大学C语言课上的杨辉 ...
- [6] 胶囊体(Capsule)图形的生成算法
顶点数据的生成 bool YfBuildCapsuleVertices ( Yreal radius, Yreal height, Yuint slices, Yuint stacks, YeOrig ...
- crtmpserver实现防盗流和流推送验证
Protecting your streams from webpage copy&paste flash code, listing or recording 保护流,防止在页面上被复制&a ...
- Word Break II leetcode java
题目: Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where e ...
- C# 的三种序列化方法
序列化是将一个对象转换成字节流以达到将其长期保存在内存.数据库或文件中的处理过程.它的主要目的是保存对象的状态以便以后需要的时候使用.与其相反的过程叫做反序列化. 序列化一个对象 为了序列化一个对象, ...
- Python 批量修改图片格式和尺寸
公司的一个项目要求把所有4096x4096的图片全部转化成2048x2048的图片,这种批量转换图片大小的软件网上很多,我的同事原来使用的美图看看的批量转换,但是稍微有点麻烦,每次还需要指定要转换的图 ...