手势解锁

这是我模仿的QQ手势解锁部分界面~

密码输入错误效果

密码输入正确效果

界面粗糙,还请海涵~

接下来开始上代码了~

我是自定义了一个SpeedDailView继承于UIView的九宫格界面

解释都在注释里了

SpeedDialView.h

#import <UIKit/UIKit.h>@interface SpeedDialView : UIView
//用来盛放选中的button
@property (nonatomic,retain) NSMutableArray *btns;
//输入的密码
@property (nonatomic,strong) NSMutableString *code;
//正确密码
@property (nonatomic,strong) NSString *password;
//触摸是否结束
@property (nonatomic,assign) BOOL isEnd;
@end

SpeedDialView.m

#import "SpeedDialView.h"@implementation SpeedDialView
- (instancetype)init
{self = [super init];if (self) {}return self;
}
//懒加载
- (NSMutableArray *)btns
{if (!_btns) {self.btns = [[NSMutableArray alloc]init];}return _btns;
}
//创建9个按钮
- (void)setUpBasicUI
{for (int i = 0; i < 9; i++) {UIButton *btn = [UIButton buttonWithType:(UIButtonTypeCustom)];btn.tag = i;[self addSubview:btn];}
}
//设置9个按钮的位置
- (void)layoutSubviews
{[super layoutSubviews];//这个方法只运行一次 static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{[self setUpBasicUI];});//经典的9宫格算法int totalColume = 3;CGFloat bWidth = 74;CGFloat bHeight = bWidth;CGFloat margin = (self.frame.size.width - bWidth * totalColume)/(totalColume + 1);for (int i = 0; i< self.subviews.count; i++) {int currentRow = i/totalColume;int currentColumn = i%totalColume;CGFloat originX = margin + (currentColumn * (margin + bWidth));CGFloat originY = currentRow * (margin + bHeight);//设置Button的外观和大小UIButton *btn = self.subviews[i];[btn setImage:[UIImage imageNamed:@"xv"] forState:(UIControlStateNormal)];[btn setImage:[UIImage imageNamed:@"shi"] forState:(UIControlStateSelected)];btn.contentMode = UIViewContentModeCenter;btn.userInteractionEnabled = false;btn.frame = CGRectMake(originX, originY, bWidth, bHeight);}}
//获取当前的触摸点的坐标
- (CGPoint)pointWithTouches:(NSSet<UITouch *> *)touches
{UITouch *touch = [touches anyObject];CGPoint point = [touch locationInView:touch.view];return point;
}
//判断当前点在不在Button中
- (UIButton *)buttonWithPoint:(CGPoint)point
{for (int i = 0; i < self.subviews.count; i++) {UIButton *btn = self.subviews[i];CGFloat wh = btn.frame.size.width - 5;//找到Button中心附近25*25的面积CGFloat frameX = btn.center.x - wh * 0.5;CGFloat frameY = btn.center.y - wh * 0.5;//判断这个点在button中心附近25*25的面积内的话就是它了if (CGRectContainsPoint(CGRectMake(frameX, frameY, wh, wh), point)) {return btn;}}return nil;
}
//设置Button被选中的状态
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{//当触摸开始的时候将之前的痕迹抹掉 重新开始self.code = [[NSMutableString alloc]init];self.isEnd = NO;for (int i = 0; i < self.btns.count; i ++) {UIButton *btn = self.btns[i];btn.selected = NO;}[self.btns removeAllObjects];//找到touch到的点的位置(此处只有一个点 就是开始的那个点)CGPoint point = [self pointWithTouches:touches];//判断这个点所在的ButtonUIButton *btn = [self buttonWithPoint:point];if (btn != nil && btn.selected == false) {btn.selected = YES;//把button加到数组中[self.btns addObject:btn];//并记录走过的路线 用Button的tag拼接出的字符串[self.code appendString:[NSString stringWithFormat:@"%ld",(long)btn.tag]];}[self setNeedsDisplay];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{//找到touch到的点的位置CGPoint point = [self pointWithTouches:touches];//判断这个点所在的ButtonUIButton *btn = [self buttonWithPoint:point];if (btn != nil && btn.selected == false) {btn.selected = YES;//把button加到数组中[self.btns addObject:btn];//并记录走过的路线 用Button的tag拼接出的字符串[self.code appendString:[NSString stringWithFormat:@"%ld",(long)btn.tag]];}[self setNeedsDisplay];
}//使用touchend清空数据
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{//绘制选中的按钮少于3个的时候没有轨迹记录if (self.btns.count < 3) {for (int i = 0; i < self.btns.count; i ++) {UIButton *btn = self.btns[i];btn.selected = NO;}[self.btns removeAllObjects];}else{//标志 结束self.isEnd = YES;}[self setNeedsDisplay];
}
//优化 先判空
-(void)drawRect:(CGRect)rect
{//如果没有选择的button 则不绘图if (self.btns.count == 0) {return;}//使用贝塞尔曲线绘制路线UIBezierPath * path = [UIBezierPath bezierPath];for (int i = 0; i < self.btns.count; i ++) {UIButton *btn = self.btns[i];if (i == 0) {[path moveToPoint:btn.center];}else{[path addLineToPoint:btn.center];}}//设置线的颜色[[UIColor colorWithRed:23/255.0 green:171/255.0 blue:227/255.0 alpha:1] set];//设置线的宽度[path setLineWidth:3];//设置连接风格[path setLineJoinStyle:(kCGLineJoinRound)];//判断是否结束 在touchEnd方法中更改isEnd的值if (_isEnd == YES) {if (![self.code isEqualToString:self.password]) {//修改Path的颜色[[UIColor redColor]set];//发送通知 提醒ViewController更改Label上的文字[[NSNotificationCenter defaultCenter] postNotificationName:@"changeLabel" object:nil];}else{//发送通知 提醒ViewController更改Label上的文字[[NSNotificationCenter defaultCenter]postNotificationName:@"changeLabel2" object:nil];}}//渲染[path stroke];}
@end

使用方法

在ViewController的Xib里设计了界面,其中最下面正方形是关联SpeedDialView的View。

ViewController.m

<span style="font-size:18px;">@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet SpeedDialView *speedDialView;@end@implementation ViewController
- (void)viewWillAppear:(BOOL)animated
{self.label.text = @"请输入手势密码";self.label.textColor = [UIColor darkGrayColor];
}
- (void)viewDidLoad {[super viewDidLoad];[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(changeLabel) name:@"changeLabel" object:nil];[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(changeLabel2) name:@"changeLabel2" object:nil];self.speedDialView.password = @"1345";}
- (void)changeLabel
{self.label.text = @"密码输入错误";self.label.textColor = [UIColor redColor];
}
- (void)changeLabel2
{self.label.text = @"恭喜,密码输入正确";self.label.textColor = [UIColor greenColor];
}
- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.
}@end
</span>

最后,就是成果展示了~

大功告成~

我对明天没有期待,因为我对今天满意~

UIButton图片提供

最后,提供Demo地址点击打开链接

我是写的OC版的,感谢简书作者王鑫20111Swift版传送门的无私分享~

实践所写,如有错误,还请指正,谢谢!

iOS Quartz2D实战系列-手把手教你手势解锁相关推荐

  1. 小白都能看懂的实战教程 手把手教你Python Web全栈开发(DAY 3)

    小白都能看懂的实战教程 手把手教你Python Web全栈开发 Flask(Python Web)实战系列之在线论坛系统 第三讲 这是小白都能看懂的实战教程 手把手教你Python Web全栈开发 的 ...

  2. 小白都能看懂的实战教程 手把手教你Python Web全栈开发(DAY 1)

    小白都能看懂的实战教程 手把手教你Python Web全栈开发 Flask(Python Web)实战系列之在线论坛系统 第一讲 博主博客文章内容导航(实时更新) 更多优质文章推荐: 收藏!最详细的P ...

  3. python流程控制-实战案例手把手教你Python流程控制技巧

    原标题:实战案例手把手教你Python流程控制技巧 作者:PianoOrRock 来源: http://blog.csdn.net/pianoorrock/article/details/711315 ...

  4. python android自动化元素定位_linux下Appium+Python移动应用自动化测试实战---3.手把手教你定位元素编写测试用例...

    linux下Appium+Python移动应用自动化测试实战-3.手把手教你定位元素编写测试用例 前言 有很多童鞋环境搭建好了却没有进行下一步,是因为缺少step by step的资料. 互联网上ap ...

  5. YoloV5实战:手把手教物体检测

    摘要:​YOLOv5并不是一个单独的模型,而是一个模型家族,包括了YOLOv5s.YOLOv5m.YOLO... 本文分享自华为云社区<YoloV5实战:手把手教物体检测--YoloV5> ...

  6. 你知道豆瓣电影是怎么评分的吗?(实战篇—手把手教你分析豆瓣电影)

    点赞再看,养成好习惯 Python版本3.8.0,开发工具:Pycharm 写在前面的话: 如果你是因为看到标题进来的,那恭喜你,又多了一个涨(入)知(坑)识的机会. 在这篇豆瓣电影Top250的分析 ...

  7. 【爬虫实战】手把手教你用Python爬取某图网4000张图片

    相信很多设计小伙伴有好的灵感,但是没有好的设计素材,今天它来了.摄图网4000张设计素材,取之不尽,如下图所示: 好了,废话不多说,开始用Python采集. 01需求分析 采集摄图网的素材图片,目标网 ...

  8. 2×3卡方检验prism_SPSS系列|手把手教你做卡方检验

    在统计学分析里,最重要的元素是数据,数据的属性和用途决定了用什么样的统计分析方法来比较数据.而数据分析工作常需借助统计软件,常见的统计软件有SPSS,SAS,S-plus,Minitab,Excel等 ...

  9. Linux——Linux驱动之设备树下platform总线驱动编写实战(手把手教你设备树下platform总线利用GPIO控制蜂鸣器完整实现过程)

    [系列专栏]:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! <QT开发实战> <嵌入式通用开发实战>

最新文章

  1. SQL Servr 2008空间数据应用系列六:基于SQLCRL的空间数据可编程性
  2. mysql表操作_MySQL表操作语句用法百科
  3. vue设置标签自定义属性_Vue组件化开发之插槽
  4. jeecg-mybatis-framework 参考学习版本
  5. 【顺序表和链表】实现增删查改数据结构 OJ题编程
  6. 【原创】通俗易懂地解决中文乱码问题(1) --- 跨平台乱码
  7. 地理信息数据 中国市级行政区划 SHP
  8. Dwg,png,jpg,Dxf格式转换
  9. VMware ESXi 7.0 正式版vSphere7.0官方原版ISO和离线定制包附加vcsa套件
  10. 主会场与分会场直播场景自由切换的实际应用效果
  11. 赛尔号找不到服务器ip,赛尔号互通版
  12. UVA 1603 Square Destroyer
  13. mysql循环方法总结
  14. C++枚举enum类型-典型性质
  15. 老夫我写代码的起手式是怎么样的?
  16. 算法笔记——基数排序
  17. 创建conda虚拟环境
  18. 软件测试面试题(软通动力,博彦科技,奇虎,瑞星,中软)
  19. java版本+企业电子招标采购系统源码+项目模块功能清单+spring cloud +spring boot
  20. 深度学习的音乐创作:回顾

热门文章

  1. python新手,作者是新手,发发微博玩玩
  2. 开源python库,cnradical获取一个字的偏旁部首和拼音
  3. 【C++】深入理解“内联与宏”
  4. parsing XML document from class path resource [applicationContext.xml]…
  5. mac os 苹果系统把ios写入u盘
  6. 基于商密SM9算法的物联网安全平台设计与应用
  7. 关于在IDEA新建中无法找到VUE组件的问题
  8. python-输出田字格图形
  9. Redis社交应用里面之关注、粉丝、共同好友案例
  10. 如何在十分钟内将阅读速度提高200%