前言:看了几篇简书,九宫格密码解锁,看着不错,拿来学习一下。
一、实现效果
实现效果二、手势解锁实现过程
分析:
#1.监听手指在view上的移动,首先肯定需要自定义一个view,重写touch began,touch move等方法,
当手指移动到圈上时,让其变亮。可以通过button按钮来实现。
#2.界面搭建--【九宫格】代码的方式创建9个按钮
1).背景图片
2).九个按钮
(把九个按钮作为一个整体,使用一个大的view来管理这些小的view,这些小的view就是9个button)。
3).新建一个类,对自定义的view进行管理,这个view是从storyboard创建出来的。
会调用aweakFrameNib方法和layoutSubviews方法,前者创造控件,后者,设置按钮frame。
4).监听手指的移动。分析程序,应该监听手指的移动,而不是按钮的点击,当手指移动到按钮的范围内时,让按钮变亮。
(1)重写touchesbegan...方法
1.获取按下的点
2.判断触摸的位置是否在按钮的范围内(使用for循环)
提示: 一个判断点是否在指定范围内的方法——CGRectContainsPoint(,);
(2)重新touchesmoved...方法
说明:当手指移动到按钮上的时候,按钮变亮,因此需要重写touchesmoved方法。
1.获取触摸的点
2.判断触摸的点是否在按钮的范围内。
提示:可以把上面两个功能分别进行封装,在使用的时候直接调用即可。
#3绘制线段
思路:获取为选中状态的按钮,并把它们存到一个数组中,重写drawRect方法,从数组中取出所有的按钮,连接所有按钮的中点。
注意:数组中不能存空值,在存储之前需要先进行判断。
新的问题:已经被连过的按钮,不能再连线。(在存储按钮的时候判断,如果该按钮已经被连线,那么就不再添加到数组中)。
绘制线段
1.获取上下文
2.取出按钮(起点和终点)
3.渲染
如图所示:
①设置控制器view背景图片
设置控制器view背景图片
②自定义view并与控制器中新拖入的view进行关联
自定义view并与控制器中新拖入的view进行关联③搭建UI
控件布局设置触摸点,实现两个代理
④创建存储选中按钮的数组,并把选中按钮添加其中,画线重绘
图4.1图4.2
- 解决问题:已经被连过的按钮,不能再连线。
解析:
1.由于每次画线的时候,我们都会调用touchbegin和touchmove方法,如果每次选中的按钮都在你触摸的范围内,都会添加到选中按钮的数组中。这样,就会造成重复添加按钮。即第二次,触摸已经选中的按钮,同样也在你触摸的范围内,这是同样也会添加到选中按钮的数组中。为了解决这个问题,我们可以在touchbegin和touchmove的判断中加一个条件 !btn.highlighted。如代码,意思是当你第二次,重复触摸同一个按钮时,如果他在你触摸的范围内且按钮的状态不是高亮状态,即向下执行。
if (CGRectContainsPoint(btn.frame, loc)&& !btn.highlighted)
2.还有个问题就是,当你在连接按钮的过程中,在空白间隙停止触摸,这样,会产生多余的线。要解决这个问题,首先我们要声明一个多余线段的点CGpoint类型。其次,获取多余线段的点,多余线段的点就等于你所触摸获取的点,进行一下关联。然后,把多余的线段画出来。最后,在touchend这个方法内,也就是当触摸完毕之后,那个多余的点,就等于,选中按钮数组中最后一个按钮的中心点。在重绘一下,就OK。
避免重复添加按钮
多余线段的解决图1
多余线段的解决图2
多余线段的解决图3
④验证密码
解析:对与验证密码这块,基本的思路是根据选中按钮的tag值,来验证用户设置的手势密码是否与之对等。换句话来说,我们添加在自定义view的按钮,当每个按钮被触碰时,都会变成高亮状态,被添加到高亮状态的数组中。手势密码也就相当于(0~9)的密码串排序。手势密码验证是在,触摸结束后验证的。所以我们要想验证密码,必须在touchend方法里遍历高亮数组获取按钮的tag值。并存入可变字符串数组中,与自己设置的手势密码字符串进行对比。
密码验证正确:按钮高亮状态消失线消失
不正确:按钮红色,线消失:按钮状态消失
密码验证1
要想线消失
高亮状态消失线消失
验证
-
代码展示:
//1.界面 ,九个按钮 , 设置frame
//2. 设置按钮的高亮状态
//3. 画线
//4. 验证密码是否正确
//5. 正确: 按钮高亮状态消失, 线消失
//6.不正确: 按钮红色, 线消失: 按钮状态消失//7. 多出来的一截线 #import "CZView.h" @interface CZView () //选中按钮的数组 /** * <#Description#> */ @property (nonatomic,strong) NSMutableArray <UIButton *> *selectedArray; //线条颜色的属性 @property(nonatomic,strong)UIColor *lineColor; //接收 多余的点 @property(nonatomic,assign)CGPoint destdationPoint; @end @implementation CZView - (UIColor *)lineColor { if (!_lineColor) { _lineColor = [UIColor whiteColor]; } return _lineColor; } - (NSMutableArray<UIButton *> *)selectedArray { if (!_selectedArray) { _selectedArray = [NSMutableArray array]; } return _selectedArray; } #pragma mark - 3.0画线 //画线 - (void)drawRect:(CGRect)rect { //创建路径 UIBezierPath *path = [UIBezierPath bezierPath]; for (NSInteger i = 0; i < self.selectedArray.count; i++) { //起点 if (i == 0) { [path moveToPoint:self.selectedArray[i].center]; }else{ [path addLineToPoint:self.selectedArray[i].center]; } //终点 } //画多出来的线 if (self.selectedArray.count > 0) { [path addLineToPoint:self.destdationPoint]; } //设置颜色 [self.lineColor set]; //渲染 [path stroke]; } #pragma mark - 2.0 设置按钮的高亮 //1. 触摸的位置 //2. 触摸的按钮 高亮 //3. 判断你触摸的点 是否在按钮的范围内: 如果是存在的 设置高亮 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //1. 触摸的位置 UITouch *touch = touches.anyObject; CGPoint loc = [touch locationInView:touch.view]; self.destdationPoint = loc; //2. 触摸的按钮 高亮 //3. 判断你触摸的点 是否在按钮的范围内: 如果是存在的 设置高亮 for (NSInteger i = 0; i < self.subviews.count; i++) { UIButton *btn = self.subviews[i]; //&& !btn.highlighted 避免重复添加 已经高亮的按钮 if (CGRectContainsPoint(btn.frame, loc) && !btn.highlighted) {//如果是存在的 //设置高亮 btn.highlighted = YES; //添加到选中按钮中 [self.selectedArray addObject:btn]; } } //重绘 [self setNeedsDisplay]; } - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //1. 触摸的位置 UITouch *touch = touches.anyObject; CGPoint loc = [touch locationInView:touch.view]; //接收 多出来的点 self.destdationPoint = loc; //2. 触摸的按钮 高亮 //3. 判断你触摸的点 是否在按钮的范围内: 如果是存在的 设置高亮 for (NSInteger i = 0; i < self.subviews.count; i++) { UIButton *btn = self.subviews[i]; if (CGRectContainsPoint(btn.frame, loc)&& !btn.highlighted) {//如果是存在的 //设置高亮 btn.highlighted = YES; //添加到选中按钮中 [self.selectedArray addObject:btn]; } } //重绘 [self setNeedsDisplay]; } #pragma mark - 4.0 验证密码 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //设置 多出来的点 在 手指抬起的时候为 选中按钮集合的最后一个 self.destdationPoint = [[self.selectedArray lastObject] center]; //1.获取密码 NSMutableString *pwd = [NSMutableString string]; for(UIButton *btn in self.selectedArray){ //拼接密码 [pwd appendFormat:@"%@",@(btn.tag)]; } //2. 验证 if([pwd isEqualToString:@"012"]){//正确 // 正确: 按钮高亮状态消失, 线消失 [self clearPath]; }else{ //不正确: 按钮红色, 线消失: 按钮状态消失 for (UIButton *btn in self.selectedArray) { //按钮的状态 不能同时存在 btn.highlighted = NO; btn.selected = YES; } //设置 线条颜色 为红色 self.lineColor = [UIColor redColor]; //重绘 [self setNeedsDisplay]; //关闭 交互 self.userInteractionEnabled = NO; //延迟 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self clearPath]; //开启交互 self.userInteractionEnabled = YES; }); } } //清空画线集合 - (void)clearPath { //将原先的红色 在设置为白色 self.lineColor = [UIColor whiteColor]; //取消按钮的高亮 for(UIButton *btn in self.selectedArray){ btn.highlighted = NO; btn.selected = NO; } //清空 画线的集合 [self.selectedArray removeAllObjects]; //重绘 [self setNeedsDisplay]; } #pragma mark - 1.0添加9个按钮 //1. aweakformnib //2. 懒加载 //创建9个按钮 - (void)awakeFromNib { for(NSInteger i = 0;i < 9;i++){ //创建按钮 UIButton *btn = [[UIButton alloc]init]; //设置tag 是为了验证密码 btn.tag = i; //关闭按钮的交互 是为了 触摸事件 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:UIControlStateSelected]; //添加 [self addSubview:btn]; } } //设置按钮的frame - (void)layoutSubviews { [super layoutSubviews]; for (NSInteger i = 0; i < self.subviews.count; i++) { //九宫格布局 CGFloat W = self.bounds.size.width; CGFloat H = self.bounds.size.height; CGFloat btnW = 74; CGFloat btnH = 74; //计算间隔 //列数 NSInteger columns = 3; // 总宽度 - 3个按钮的宽度 / 2 CGFloat margW = (W - columns * btnW)/(columns - 1); CGFloat margH = margW; //列的索引 NSInteger col = i % columns; //行的索引 NSInteger row = i / columns; CGFloat btnX = col * (margW + btnW); CGFloat btnY = row * (margH + btnH); //设置按钮的frame UIButton *btn = self.subviews[i]; btn.frame = CGRectMake(btnX, btnY, btnW, btnH); }}
知识点补充
1.九宫格实现原理
界面是一个九宫格的布局.九宫格实现思路.
1.先确定有多少列 cloum = 3;
2.计算出每列之间的距离
2.1计算为: CGFloat margin = (当前View的宽度 - 列数 * 按钮的宽度) / (总列数 - 1)
3.每一列的X的值与它当前所在的列有关
3.1列号:curColum = i % cloum
4.每一行的Y的值与它当前所在的行有关
4.1行号:curRow = i / cloum
5.每一个按钮的X值为, margin + 当前所在的列 * (按钮的宽度+ 每个按钮之间的间距)
6.每一个按钮的Y值为 当前所在的行 * (按钮的宽度 + 每个按钮之间的距离)
网友评论
self.destdationPoint = [[self.buttonSelectedArr lastObject]center];
会报错,请问是大概会有什么问题