UI进阶--Quartz2D和触摸事件的简单使用:手势解锁

需求:实现一个简易的手势解锁应用,具体效果如下图所示:

实现步骤:

1、代码创建界面,自定义一个view,设置view的背景,颜色等属性;

2在自定义的view中,定义2个属性,一个是存储被选中按钮的可变数组,另外一个是最后的触摸点(CGPoint);

3、重写initWithFrame方法,在这里,自定义一个方法给initWithFrame方法调用即可,这个自定义的方法里,初始化9个按钮,设置每个按钮的tag,正常状态下的图片以及选中状态的图片,并设置和用户的交互为NO

4在自定义的view中的layoutSubviews方法中,自定义9个按钮;

5、在touchesBegan:touchesMoved:方法中,判断触摸点是否在view上的9个按钮中,如果在,那么设置按钮的选中状态;(在这里,只写其中一个方法即可,另外一个方法直接调用)

5.1、进行遍历,并把选中的按钮放在选中按钮的可变数组中,并记录最后的触摸点;

5.2、在最后记得重绘图形,调用setNeedsDisplay方法;

6、在drawRect:方法中,绘制选中按钮之间的连线;

6.1、遍历选中按钮数组,得到选中按钮的数量,如果为0,直接返回;

6.2、使用UIBezierPath画线;

6.3、对绘制的线进行线宽,颜色、样式等设置

6.4、对绘制的线进行渲染;

7、在touchesEnded:方法中取消连线,并移除选中的按钮,最后重绘图形;

8、选择完密码后,把密码传回给控制器,让控制器判断选择是否正确;

8.1、在自定义的view中设置代理;

8.2、在touchesEnded:方法中拼接选中按钮的索引;

8.3、通知代理按钮的选择;

具体实现代码:

 

Controller:

 1 //
 2 //  ViewController.m
 3 //  1-4-Deblocking
 4 //
 5 //  Created by xiaomoge on 15/1/4.
 6 //  Copyright (c) 2015年 xiaomoge. All rights reserved.
 7 //
 8 
 9 #import "ViewController.h"
10 #import "DeblockingView.h"
11 @interface ViewController () <DeblockingViewDelegate>
12 
13 @end
14 
15 @implementation ViewController
16 
17 - (void)viewDidLoad {
18     [super viewDidLoad];
19     //设置背景颜色
20     self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Home_refresh_bg"]];
21     //取得当前屏幕的宽度
22     CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
23     //初始化一个DeblockingView
24     DeblockingView *deblockingView = [[DeblockingView alloc] initWithFrame:CGRectMake(0, 0, screenW, screenW)];
25     //设置背景颜色
26     deblockingView.backgroundColor = [UIColor clearColor];
27    //居中显示
28     deblockingView.center = self.view.center;
29    //设置代理
30     deblockingView.delegate = self;
31     [self.view addSubview:deblockingView];
32 }
33 #pragma mark - DeblockingViewDelegate方法
34 - (void)selectBtn:(DeblockingView *)deblockingView andSelectPwd:(NSString *)pwd {
35     NSString *tips;
36     if ([pwd isEqualToString:@"03478"]) {//设置一个固定的密码
37         tips = @"解锁成功";
38     }else {
39         tips = @"密码错误";
40     }
41     //弹出窗口提示
42     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:tips delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
43     [alert show];
44 }
45 @end

View:

 1 //
 2 //  DeblockingView.h
 3 //  1-4-Deblocking
 4 //
 5 //  Created by xiaomoge on 15/1/4.
 6 //  Copyright (c) 2015年 xiaomoge. All rights reserved.
 7 //
 8 
 9 #import <UIKit/UIKit.h>
10 @class DeblockingView;
11 @protocol DeblockingViewDelegate <NSObject>
12 @optional
13 - (void)selectBtn:(DeblockingView *)deblockingView andSelectPwd:(NSString *)pwd;
14 
15 @end
16 @interface DeblockingView : UIView
17 @property (nonatomic,assign) id<DeblockingViewDelegate> delegate;
18 @end
  1 //
  2 //  DeblockingView.m
  3 //  1-4-Deblocking
  4 //
  5 //  Created by xiaomoge on 15/1/4.
  6 //  Copyright (c) 2015年 xiaomoge. All rights reserved.
  7 //
  8 
  9 #import "DeblockingView.h"
 10 @interface DeblockingView ()
 11 /*
 12  选中的按钮数组
 13  */
 14 @property (nonatomic,strong) NSMutableArray *selectedBtns;
 15 /*
 16  最后触摸到的点
 17  */
 18 @property (nonatomic,assign) CGPoint lastPoint;
 19 @end
 20 @implementation DeblockingView
 21 //懒加载
 22 - (NSMutableArray *)selectedBtns {
 23     if (!_selectedBtns) {
 24         _selectedBtns = [NSMutableArray array];
 25     }
 26     return _selectedBtns;
 27 }
 28 //绘制选中按钮间的连线
 29 - (void)drawRect:(CGRect)rect {
 30     //取得当前选中的按钮的个数
 31     NSInteger selectBtnCount = self.selectedBtns.count;
 32     //如果被选中的按钮个数为0,即返回。
 33     if (selectBtnCount == 0) return;
 34     
 35     UIBezierPath *path = [UIBezierPath bezierPath];
 36     for (NSInteger i = 0; i < selectBtnCount; i++) {
 37         //取得按钮先连接的点
 38         CGPoint btnCenter = [self.selectedBtns[i] center];
 39         if (i == 0) {//设置为起点
 40             [path moveToPoint:btnCenter];
 41         }else {//设置为连接点
 42             [path addLineToPoint:btnCenter];
 43         }
 44     }
 45     //如果最后有未选中按钮的连线点
 46     [path addLineToPoint:self.lastPoint];
 47     //设置连线的宽度
 48     path.lineWidth = 8;
 49     //设置连线的头尾样式
 50     path.lineCapStyle = kCGLineCapRound;
 51     //设置连线的连接点样式
 52     path.lineJoinStyle = kCGLineJoinRound;
 53     //设置连线的颜色
 54     [[UIColor greenColor] set];
 55     //渲染
 56     [path stroke];
 57 }
 58 //加载完view后会调用这个方法
 59 - (void)layoutSubviews {
 60     [super layoutSubviews];//别忘记要写父类的方法
 61     //取得按钮的数量
 62     NSInteger btnCount = self.subviews.count;
 63     //设置按钮的宽度
 64     CGFloat btnW = 74;
 65     //设置按钮的高度
 66     CGFloat btnH = 74;
 67     //设置按钮间的间距
 68     CGFloat margin = (self.bounds.size.width - 3 * btnW) / 4;
 69     for (int i = 0; i < btnCount; i++) {
 70         //取得一个按钮
 71         UIButton *btn = self.subviews[i];
 72         //获得当前的行号
 73         int row = i / 3;
 74         //获得当前列号
 75         int culomns = i % 3;
 76         //设置按钮的X(可参考应用管理中的计算)
 77         CGFloat btnX = margin + culomns * (margin + btnW);
 78         //设置按钮的Y
 79         CGFloat btnY = margin + row * (margin + btnH);
 80         btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
 81     }
 82 }
 83 //初始化方法
 84 - (instancetype)initWithFrame:(CGRect)frame {
 85     if (self = [super initWithFrame:frame]) {
 86         [self setBtns];
 87     }
 88     return self;
 89 }
 90 //设置9个按钮
 91 - (void)setBtns {
 92     for (int i = 0; i < 9; i++) {
 93         UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
 94         btn.tag = i;
 95         //设置按钮正常状态下的图片
 96         [btn setImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
 97         //设置按钮被选中时的图片
 98         [btn setImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
 99         //取消与用户的交互
100         btn.userInteractionEnabled = NO;
101         [self addSubview:btn];
102     }
103 }
104 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
105     [self touchesMoved:touches withEvent:event];
106 }
107 
108 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
109     //获取当前触摸点
110     UITouch *touch = [touches anyObject];
111     CGPoint touchPoint = [touch locationInView:touch.view];
112     
113     for (UIButton *btn in self.subviews) {
114         if (CGRectContainsPoint(btn.frame, touchPoint)) {//判断触摸点是否在按钮的范围内
115             if (btn.selected == NO) {//在按钮范围内,并且为未选中状态时
116                 [self.selectedBtns addObject:btn];//加进选中按钮的数组
117             }
118             btn.selected = YES;
119         }else {//否则设置这个点为最后的触摸点
120             self.lastPoint = touchPoint;
121         }
122     }
123     //重绘
124     [self setNeedsDisplay];
125 }
126 
127 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
128     //拼接选中按钮的tag作为密码
129     NSMutableString *pwd = [NSMutableString string];
130     for (UIButton *btn in self.selectedBtns) {
131         [pwd appendFormat:@"%ld",btn.tag];
132     }
133     //如果代理实现了方法,通知代理对象
134     if ([self.delegate respondsToSelector:@selector(selectBtn:andSelectPwd:)]) {
135         [self.delegate selectBtn:self andSelectPwd:pwd];
136     }
137     //设置所有被选中的按钮为未选中状态
138     [self.selectedBtns makeObjectsPerformSelector:@selector(setSelected:) withObject:@NO];
139     //移除所有被选中的按钮
140     [self.selectedBtns removeAllObjects];
141     //重绘
142     [self setNeedsDisplay];
143 }
144 @end

最后的效果图:

原文地址:https://www.cnblogs.com/xiaomoge/p/4202240.html