iOS開發之手勢解鎖,ios手勢解鎖
本文主要介紹通過手勢識別實現手勢解鎖功能,這個方法被廣泛用於手機解鎖,密碼驗證,快捷支付等功能實現。案例效果如下所示。
首先,我們先分析功能的實現過程,首先我們需要先看大致的實現過程:
1.載入九宮格頁面
2.實現按鈕被點擊及滑動過程中按鈕狀態的改變
3.實現滑動過程中的連線
4.繪製完畢後判定密碼是否正確,
5.密碼判定後實現跳轉。
下面我們就來用代碼實現上述五個過程。
1.載入九宮格介面
1.1九宮格內控制項的分布 3*3 ,我們可以自訂view(包含3*3個按鈕),添加到viewController上。
//添加view中子控制項-(void)awakeFromNib{// 建立按鈕 for (int i=0; i<9; i++) { self.LineColor=[UIColor blueColor]; UIButton *btn=[UIButton buttonWithType:UIButtonTypeCustom]; 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:UIControlStateDisabled]; [self addSubview:btn]; }}//布局view子控制項-(void)layoutSubviews{ [super layoutSubviews]; CGFloat width=74; CGFloat height=74; CGFloat Margin=(self.bounds.size.width-3*width)/2;// 遍曆設定9個button的frame [self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {// 通過tag設定按鈕的索引標識 obj.tag=idx; int row=(int)idx/3; int col=idx%3; obj.frame=CGRectMake(col*(Margin + width), row*(Margin +height), width, height); }];}
1.2將定義好的view通過xib添加到viewController上
首先,定義一個blockview(九宮格view)的類方法,
// 載入xib檔案+(instancetype)lockView{ return [[[NSBundle mainBundle]loadNibNamed:@"MYblockView" owner:nil options:nil]lastObject];}
然後載入到控制器上。
// 設定控制器view的背景圖片 self.view.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:@"bg"]]; MYblockView *blockView=[MYblockView lockView]; blockView.center=self.view.center;// 將blockview添加到viewController上 [self.view addSubview:blockView];
2.實現按鈕被點擊及滑動過程中按鈕狀態的改變
2.1定義數群組類型的成員屬性,用來裝被點擊的按鈕
@property(nonatomic,strong)NSMutableArray *btnArr;//懶載入-(NSMutableArray *)btnArr{ if (_btnArr==nil) { _btnArr=[NSMutableArray array]; } return _btnArr;}
2.2建立路徑,繪製圖形
#pragma mark----繪製圖形-(void)drawRect:(CGRect)rect{ if (self.btnArr.count==0 ) { return; }// 建立路徑 UIBezierPath *path=[UIBezierPath bezierPath];// 遍曆所有按鈕進行繪製 [self.btnArr enumerateObjectsUsingBlock:^(__kindof UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {// 第一個按鈕,中心點就是起點 if (idx==0) { [path moveToPoint:obj.center]; }else { [path addLineToPoint:obj.center]; } }]; [path addLineToPoint:self.currentPoint];// 設定路徑屬性 path.lineWidth=10; path.lineCapStyle=kCGLineCapRound; path.lineJoinStyle=kCGLineJoinRound; [self.LineColor setStroke];// 渲染 [path stroke];}
2.3開始觸摸
#pragma mark-----開始觸摸-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{// 擷取觸摸對象 UITouch *touch=touches.anyObject;// 擷取觸摸點 CGPoint loc=[touch locationInView:self];// 遍曆按鈕,判定觸摸點是否在按鈕上 [self.subviews enumerateObjectsUsingBlock:^(__kindof UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { BOOL isContains=CGRectContainsPoint(obj.frame, loc);// 如果在按鈕上,將當前按鈕儲存在數組中,並改變按鈕狀態 if (isContains&&obj.highlighted==NO) { [self.btnArr addObject:obj]; obj.highlighted=YES; }else { obj.highlighted=NO; } }];}
2.4滑動過程中,重繪
#pragma mark----開始滑動-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{// 擷取觸摸對象 UITouch *touch=touches.anyObject;// 擷取觸摸點 CGPoint loc=[touch locationInView:self]; self.currentPoint=loc;// 遍曆按鈕,如果按鈕在滑動路徑上,就改變按鈕狀態 [self.subviews enumerateObjectsUsingBlock:^(__kindof UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { BOOL isContains=CGRectContainsPoint(obj.frame, loc); if (isContains&&obj.highlighted==NO) { [self.btnArr addObject:obj]; obj.highlighted=YES; } }];// 重繪 [self setNeedsDisplay]; }
3.實現滑動過程中的連線和4.繪製完畢後判定密碼是否正確,
#pragma mark----停止滑動結束-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{// 定義最後一個按鈕 UIButton *lastBtn=[self.btnArr lastObject];// 將最後一個按鈕中心點定義為相對滑動的當前點 self.currentPoint=lastBtn.center;// 重繪 [self setNeedsDisplay];// 判定密碼 self.password=[NSMutableString string]; [self.btnArr enumerateObjectsUsingBlock:^( UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [self.password appendFormat:@"%@",@(obj.tag)]; }]; NSLog(@"%@",self.password); BOOL isOk; if ([self.delegate respondsToSelector:@selector(blockView:finishedWithPassword:)]) { isOk= [self.delegate blockView:self finishedWithPassword:self.password]; } if (isOk) { [self.btnArr enumerateObjectsUsingBlock:^(UIButton* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { obj.highlighted=NO; }]; [self.btnArr removeAllObjects]; [self setNeedsDisplay]; NSLog(@"密碼正確"); }else { NSLog(@"密碼錯誤"); }}
注意:我們在密碼判定過程中是通過根據先前布局按鈕的時候定義的按鈕tag值進行字串拼接,密碼傳值是通過代理實現。
#import <UIKit/UIKit.h>@class MYblockView;//聲明代理@protocol MYblockViewDelegate <NSObject>@optional//代理方法-(BOOL) blockView:(MYblockView *)blockView finishedWithPassword:(NSString *)password;@end@interface MYblockView : UIView+(instancetype)lockView;//設定代理成員屬性@property(nonatomic,weak)id<MYblockViewDelegate>delegate;@end
5.密碼判定後實現跳轉。
else { // 關閉使用者互動 self.userInteractionEnabled=NO; [self.btnArr enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { self.LineColor=[UIColor redColor]; obj.highlighted=NO; obj.enabled=NO; [self setNeedsDisplay]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 回複按鈕狀態 [self.btnArr enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { obj.enabled=YES; }];// 恢複線條的顏色 self.LineColor=[UIColor blueColor]; [self.btnArr removeAllObjects]; [self setNeedsDisplay]; }); }]; NSLog(@"密碼錯誤"); } self.userInteractionEnabled=YES;}
代理判定密碼並實現跳轉
-(BOOL)blockView:(MYblockView *)blockView finishedWithPassword:(NSString *)password{ if ([password isEqualToString:@"012"]) { UIViewController *two=[UIViewController new]; two.view.backgroundColor=[UIColor greenColor]; [self.navigationController pushViewController:two animated:YES]; return YES; } else{ return NO; }}
最後設定控制器navigationbar屬性
[self.navigationController.navigationBar setBackgroundColor:[UIColor redColor]]; [ self.navigationController.navigationBar setTitleTextAttributes:@{ NSForegroundColorAttributeName :[UIColor whiteColor] }];