最终效果图:
控制器:
// // BeyondViewController.m // 39_触摸解锁 // // Created by beyond on 14-9-17. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "BeyondViewController.h" #import "LockView.h" #import "LockViewDelegate.h" // 遵守协议,用于获知,用户在LockView上画的解锁路径 @interface BeyondViewController ()<LockViewDelegate> @end @implementation BeyondViewController #pragma mark - LockView代理方法 // 用于获知,用户在LockView上画的解锁路径 - (void)lockView:(LockView *)lockView didFinishPath:(NSString *)path { [MBProgressHUD showSuccess:path]; NSLog(@"获得用户的手势路径:%@", path); } @end<span style="font-family:Courier New;color:#393939;"><span style="font-size: 24px; line-height: 26px; background-color: rgb(245, 245, 245);"><strong> </strong></span></span>
自定义CircleButton
// // Circle.m // 39_触摸解锁 // // Created by beyond on 14-9-17. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "Circle.h" @implementation Circle - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 自定义方法,初始化 [self setup]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { // 自定义方法,初始化 [self setup]; } return self; } // 自定义方法,初始化 - (void)setup { // 设置按钮不可用(不可点击) self.userInteractionEnabled = NO; // 设置默认的背景图片 [self setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal]; // 设置选中时的背景图片(selected) [self setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected]; } @end
LockView定义的协议
// // LockViewDelegate.h // 39_触摸解锁 // // Created by beyond on 14-9-17. // Copyright (c) 2014年 com.beyond. All rights reserved. // 自定义的LockView的代理必需 遵守的协议 #import <Foundation/Foundation.h> @class LockView; @protocol LockViewDelegate <NSObject> @optional // 锁屏手势绘制完成时,调用本方法,通知外界 - (void)lockView:(LockView *)lockView didFinishPath:(NSString *)path; @end
核心代码,自定义的LockView
// // LockView.h // 39_触摸解锁 // // Created by beyond on 14-9-17. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import <UIKit/UIKit.h> @protocol LockViewDelegate; @interface LockView : UIView // 代理,为当前控制器,目的是,解锁手势绘制完成后,通知它 @property (nonatomic, weak) IBOutlet id<LockViewDelegate> delegate; @end
// // LockView.m // 39_触摸解锁 // // Created by beyond on 14-9-17. // Copyright (c) 2014年 com.beyond. All rights reserved. // 重点,核心~~~ #import "LockView.h" #import "Circle.h" #import "LockViewDelegate.h" @interface LockView() // 数组,记住所有的touchMove经过的circle @property (nonatomic, strong) NSMutableArray *circleArr; // 手指一直与屏幕保持触摸的点,活动的点,用于绘制最后一个circle的中心到 用户触摸点的线段 @property (nonatomic, assign) CGPoint activePoint; @end @implementation LockView #pragma mark - 懒加载 // 数组,记住所有的touchMove经过的circle - (NSMutableArray *)circleArr { if (_circleArr == nil) { _circleArr = [NSMutableArray array]; } return _circleArr; } #pragma mark - 初始化 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 调用自定义方法,一次性添加所有的按钮 [self addAllCircles]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]){ // 调用自定义方法,一次性添加所有的按钮 [self addAllCircles]; } return self; } // 调用自定义方法,一次性添加所有的按钮 - (void)addAllCircles { for (int index = 0; index<9; index++) { // 创建自定义按钮,绑定tag目的是,将来用于验证锁的顺序 Circle *btn = [Circle buttonWithType:UIButtonTypeCustom]; btn.tag = index; // 添加按钮 [self addSubview:btn]; } } #pragma mark - 父类方法 // 每添加一个circle,就调整一下所有子控件的frame - (void)layoutSubviews { // 必须先调用父类的方法 [super layoutSubviews]; // 遍历所有的子按钮,重新调整frame for (int i = 0; i<self.subviews.count; i++) { // 取出按钮 Circle *btn = self.subviews[i]; // 设置frame CGFloat btnW = 74; CGFloat btnH = 74; // 共三列 int totalColumns = 3; // 第i个按钮 所在的列 int col = i % totalColumns; // 第i个按钮 所在的行 int row = i / totalColumns; // 间距均分 CGFloat marginX = (self.frame.size.width - totalColumns * btnW) / (totalColumns + 1); CGFloat marginY = marginX; // 设置frame CGFloat btnX = marginX + col * (btnW + marginX); CGFloat btnY = row * (btnH + marginY); btn.frame = CGRectMake(btnX, btnY, btnW, btnH); } } #pragma mark - 触摸方法 // 手指按下 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 1.必须先清空上一次(手指与屏幕一直接触的点) self.activePoint = CGPointZero; // 2.调用自定义方法,获得触摸开始的这个点 CGPoint startPoint = [self pointWithTouches:touches]; // 3.调用自定义方法,获得触摸到的按钮(如果有的话) Circle *btn = [self buttonWithPoint:startPoint]; // 4.设置该被触摸到的按钮的状态(如果有) if (btn && btn.selected == NO) { btn.selected = YES; // 按钮数组中没有,才要添加到对象数组 [self.circleArr addObject:btn]; } // 5.刷新界面,下面方法会自动调用drawRect [self setNeedsDisplay]; } // 手指移动(调用频率相当高) - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { // 1.调用自定义方法,获得触摸过程中的点 CGPoint pos = [self pointWithTouches:touches]; // 2.调用自定义方法,获得触摸移动时碰到的按钮(如果有的话) Circle *btn = [self buttonWithPoint:pos]; // 3.设置该被触摸移动时碰到的按钮的状态(如果有) if (btn && btn.selected == NO) { // 摸到了按钮 btn.selected = YES; // 按钮数组中没有,才要添加到对象数组 [self.circleArr addObject:btn]; } else { // 没有摸到按钮,就更新activePoint的位置,以便drawRect self.activePoint = pos; } // 4.刷新界面,下面方法会自动调用drawRect [self setNeedsDisplay]; } // 触摸结束(手抬起了),通知代理, - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { // 1.通知代理 if ([self.delegate respondsToSelector:@selector(lockView:didFinishPath:)]) { NSMutableString *path = [NSMutableString string]; for (Circle *btn in self.circleArr) { // tag此时,起到关键作用 [path appendFormat:@"%d", btn.tag]; } [self.delegate lockView:self didFinishPath:path]; } // 2.可有可无,清空所有的按钮选中状态(恢复原样) [self.circleArr makeObjectsPerformSelector:@selector(setSelected:) withObject:@(NO)]; // 3.清空选中的按钮对象数组(为下一次手指按下做准备) [self.circleArr removeAllObjects]; // 4.刷新界面,下面方法会自动调用drawRect [self setNeedsDisplay]; } // 触摸被打断(类似结束) - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [self touchesEnded:touches withEvent:event]; } #pragma mark - 抽取的自定义方法 // 自定义方法,根据touches集合获得对应的触摸点位置坐标 - (CGPoint)pointWithTouches:(NSSet *)touches { UITouch *touch = [touches anyObject]; // 返回手指触摸点在对应view中的坐标 return [touch locationInView:touch.view]; } // 自定义方法,根据触摸点位置坐标,遍历所有按钮,获得被碰手指到的按钮(如果有) - (Circle *)buttonWithPoint:(CGPoint)point { for (Circle *btn in self.subviews) { // CGFloat wh = 24; // CGFloat frameX = btn.center.x - wh * 0.5; // CGFloat frameY = btn.center.y - wh * 0.5; // if (CGRectContainsPoint(CGRectMake(frameX, frameY, wh, wh), point)) { if (CGRectContainsPoint(btn.frame, point)) { return btn; } } return nil; } #pragma mark - 核心~绘图 // 最后一步 - (void)drawRect:(CGRect)rect { // 如果没有碰到任何按钮,直接返回,不用绘线 if (self.circleArr.count == 0) return; // 1.开始一个贝塞尔路径 UIBezierPath *path = [UIBezierPath bezierPath]; // 2.遍历所有的按钮,将碰到的按钮的中心添加到路径中 for (int i = 0; i<self.circleArr.count; i++) { Circle *btn = self.circleArr[i]; if (i == 0) { // 设置路径起点 [path moveToPoint:btn.center]; } else { // 添加到路径 [path addLineToPoint:btn.center]; } } // 3.连接最后一条,即最后一个碰到的按钮中心 至 activePoint if (CGPointEqualToPoint(self.activePoint, CGPointZero) == NO) { // 必须先进行检测 [path addLineToPoint:self.activePoint]; } // 4.设置绘图属性 path.lineWidth = 8; // 圆接头 path.lineJoinStyle = kCGLineJoinBevel; // 线条天蓝色 [[UIColor colorWithRed:32/255.0 green:210/255.0 blue:254/255.0 alpha:0.5] set]; // 5.闭合贝塞尔路径 [path stroke]; } @end
storyboard
时间: 2024-10-17 06:29:38