需求:实现一个简易的手势解锁应用,具体效果如下图所示:
实现步骤:
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
最后的效果图: