自定义手势解锁锁控件
一、控件的使用
模仿市面上app的手勢(shì)解鎖功能,實(shí)現(xiàn)的小控件,將控件封裝到了一個(gè)UIView上
?
?
二、核心原理技術(shù)
?1、觸摸事件
(1)UIView的觸摸三個(gè)觸摸響應(yīng)事件:開始、移動(dòng)、結(jié)束
(2)CGRectContainsPoint 判斷觸摸點(diǎn)的位置
?
2、Quartz2D繪圖
(1)drawRect 的重繪
?
(2)UIBezierPath 貝塞爾曲線
?
3、block成功和失敗的回調(diào)
?
?
三、實(shí)現(xiàn)思路
1、解鎖鍵盤中的9個(gè)小圖標(biāo),會(huì)根據(jù)驗(yàn)證過(guò)程而變化顏色,所以考慮用UIButton實(shí)現(xiàn),因?yàn)閁IButton可以根據(jù)設(shè)置不同狀態(tài),而獲得不同的圖片。按鈕本身不需要點(diǎn)擊事件的實(shí)現(xiàn)。
?
2、觸摸過(guò)程中,實(shí)現(xiàn)觸摸事件的三個(gè)方法:
(1)在開始時(shí)判斷觸摸點(diǎn)是否在某個(gè)按鈕上,進(jìn)而改變按鈕的狀態(tài),從而實(shí)現(xiàn)“點(diǎn)亮”。
(2)建立數(shù)組,記錄每一個(gè)被“點(diǎn)亮”的按鈕
(3)按照按鈕的中心點(diǎn)繪制連線
(4)移動(dòng)過(guò)程中,繼續(xù)“點(diǎn)亮”按鈕并記錄
(5)觸摸結(jié)束,進(jìn)行邏輯判斷,是否解鎖成功,解鎖密碼用按鈕的tag拼接(整數(shù)串)。根據(jù)成功與否,更改界面上按鈕的狀態(tài),然后再重繪
(6)進(jìn)行成功或失敗的回調(diào)
?
四、源碼
?
1、.h文件
?
@interface ZQGestureUnlockView : UIView/*** 實(shí)例化解鎖鍵盤,寬高320*320,9個(gè)按鈕,背景黑** @param frame x,y可用* @param password 輸入密碼由1~9的數(shù)組組成的字符串,不可重復(fù)* @param success 解鎖成功的回調(diào)* @param fail 解鎖失敗的回調(diào)** @return 返回手勢(shì)鎖鍵盤*/ +(instancetype)unlockWithFrame:(CGRect)frame Password:(NSString *)password successBlock:(void(^)())success failBlock:(void(^)())fail;@end?
?
2、宏定義及私有變量定義
?
//按鈕顯示列數(shù) #define col 3//按鈕總數(shù) #define sum 9//按鈕的寬高 #define iconWH 80//默認(rèn)狀態(tài)下連線的顏色 #define ZQLineColor [UIColor colorWithRed:0.0 green:170/255.0 blue:255/255.0 alpha:0.5]@interface ZQGestureUnlockView ()//記錄選中的按鈕 @property(nonatomic,strong) NSMutableArray * clickBtnArray;//記錄連線的顏色 @property(nonatomic,strong) UIColor * lineColor;//記錄時(shí)刻的觸摸點(diǎn)坐標(biāo),時(shí)刻是最新的點(diǎn) @property(nonatomic,assign) CGPoint currentPoint;//用戶指定的密碼 @property(nonatomic,copy) NSString * password;//成功回調(diào)的block @property(nonatomic,copy) void(^success)();//失敗回調(diào) block @property(nonatomic,copy) void(^fail)();@end?
3、.m文件中的方法實(shí)現(xiàn)
?
//初始化方法 +(instancetype)unlockWithFrame:(CGRect)frame Password:(NSString *)password successBlock:(void(^)())success failBlock:(void(^)())fail{ZQGestureUnlockView * mainView = [[self alloc]init]; mainView.frame = CGRectMake(frame.origin.x, frame.origin.y, 320, 320);mainView.password = password;mainView.success = success; mainView.fail = fail;//生成按鈕 [mainView setupButtons];return mainView; }//生成按鈕 -(void)setupButtons {//生成按鈕for (int i = 1; i<sum+1; i++) {UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom]; [button setImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];[button setImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateHighlighted];[button setImage:[UIImage imageNamed:@"gesture_node_error"] forState:UIControlStateDisabled];//增加按鈕的tagbutton.tag = i ;//初始狀態(tài),讓按鈕不可交互,按鈕就是顯示用的,沒(méi)有點(diǎn)擊事件button.userInteractionEnabled=NO; [self addSubview:button];}//記錄一下默認(rèn)的線的顏色self.lineColor = ZQLineColor;//設(shè)置透明self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bg"]]; }//布局子控件 -(void)layoutSubviews {[super layoutSubviews];CGFloat margin = (self.frame.size.width - col*iconWH)/2;[self.subviews enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {CGFloat x= idx%col *(iconWH + margin);CGFloat y= idx/col * (iconWH +margin);obj.frame=CGRectMake(x, y, iconWH, iconWH);}];}#pragma mark - 觸摸開始,記錄開始的點(diǎn) -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {UITouch * touch = touches.anyObject; CGPoint startPoint = [touch locationInView:self];//遍歷所有按鈕,判斷觸摸的位置是否在button范圍內(nèi) [self.subviews enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {BOOL isContain = CGRectContainsPoint(obj.frame, startPoint);//如果在某個(gè)按鈕范圍內(nèi),同時(shí)這個(gè)按鈕沒(méi)有被點(diǎn)亮if (isContain && obj.highlighted==NO) {//改變按鈕狀態(tài)為高亮obj.highlighted = YES;//將這個(gè)按鈕記錄到“選中按鈕標(biāo)記數(shù)組” [self.clickBtnArray addObject:obj]; }//如果不在某個(gè)按鈕內(nèi),那么把這個(gè)按鈕設(shè)置為不高亮(不管它是否原來(lái)是高亮狀態(tài))else{obj.highlighted=NO;} }];//保存此刻的坐標(biāo),用于繪制連線self.currentPoint = startPoint; }#pragma mark - 觸摸移動(dòng),同樣判斷按鈕的位置 -(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {UITouch * touch = touches.anyObject;CGPoint movePoint = [touch locationInView:self];//判斷觸摸move的位置是否在button范圍內(nèi),遍歷所有按鈕[self.subviews enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {BOOL isContain = CGRectContainsPoint(obj.frame, movePoint);//如果觸摸范圍在某個(gè)按鈕上,并且該按鈕沒(méi)有被點(diǎn)亮if (isContain && obj.highlighted==NO) { obj.highlighted = YES;[self.clickBtnArray addObject:obj]; } }];//保存此刻的坐標(biāo)self.currentPoint = movePoint; //調(diào)用view重繪,繪制連線 [self setNeedsDisplay]; }#pragma mark - 觸摸結(jié)束,進(jìn)行邏輯判斷,重繪連線,顯示判斷后的連線狀態(tài) -(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {//拼裝用戶手勢(shì)結(jié)束后的密碼NSMutableString * passWordString =[NSMutableString string]; [self.clickBtnArray enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { //合成密碼[passWordString appendFormat:@"%ld",obj.tag];}];//證明密碼正確if ([passWordString isEqualToString:self.password]) {//加延遲,是為了密碼正確后給一個(gè)短暫的停頓dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{//先把按鈕取消高亮[self.clickBtnArray enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {obj.highlighted=NO; }];//再把數(shù)組清空 [self.clickBtnArray removeAllObjects];//最后重繪 [self setNeedsDisplay];});//執(zhí)行驗(yàn)證成功的回調(diào) self.success();}//密碼錯(cuò)誤else{//首先關(guān)閉view交互,避免錯(cuò)誤操作self.userInteractionEnabled=NO;//先把按鈕高亮去掉,設(shè)置成enabled狀態(tài),然后把連線變紅[self.clickBtnArray enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {obj.enabled=NO;obj.highlighted=NO;}];self.lineColor = [UIColor redColor];[self setNeedsDisplay];//延遲一下再清除dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{//先把按鈕enable[self.clickBtnArray enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {obj.enabled=YES;}];//再把高亮按鈕數(shù)組清空 [self.clickBtnArray removeAllObjects];//重繪 [self setNeedsDisplay];//最后在開啟交互self.userInteractionEnabled=YES;//還原默認(rèn)連線顏色self.lineColor=ZQLineColor;});}//將currentPoint 設(shè)置成和最后的高亮按鈕中心一樣,這樣可以在釋放觸摸后,連線不顯示多余的“尾巴”UIButton * btn = self.clickBtnArray.lastObject;self.currentPoint= btn.center;}//view的重繪 -(void)drawRect:(CGRect)rect {UIBezierPath * path = [UIBezierPath bezierPath];[self.clickBtnArray enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {//設(shè)置起始點(diǎn)if (idx==0) {[path moveToPoint:obj.center];}//設(shè)置增加點(diǎn)else{[path addLineToPoint:obj.center];}}];//線條顏色、寬[path setLineWidth:10];UIColor * color = self.lineColor;[color setStroke];//設(shè)置連接 [path setLineJoinStyle:kCGLineJoinRound];[path setLineCapStyle:kCGLineCapRound];//再添加實(shí)時(shí)的“線頭”if (self.clickBtnArray!=nil) {[path addLineToPoint:self.currentPoint];}//繪圖 [path stroke]; }//懶加載 -(NSMutableArray *)clickBtnArray {if (!_clickBtnArray) {_clickBtnArray=[NSMutableArray array];}return _clickBtnArray; }
?
?
五、擴(kuò)展
1、解鎖鍵盤的大小可以根據(jù)實(shí)際情況定義
2、觸摸手勢(shì)的繪制采用的是不可重復(fù)的點(diǎn),這個(gè)可以改進(jìn)
3、不限驗(yàn)證次數(shù)?
4、本例用于娛樂(lè)、學(xué)習(xí)、交流
轉(zhuǎn)載于:https://www.cnblogs.com/cleven/p/5374429.html
總結(jié)
以上是生活随笔為你收集整理的自定义手势解锁锁控件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Mac OS使用技巧十九:Safari碉
- 下一篇: HDU 2393 Higher Math