iOS指紋解鎖和手勢(shì)解鎖

iOS指紋解鎖和手勢(shì)解鎖

[TOC]

前言

一直想寫(xiě)博客來(lái)著,一來(lái)可以記錄一些自己學(xué)習(xí)和研究的東西,二來(lái)也可以將自己寫(xiě)的一些東西分享出去,給他人參考,還可能收到他人的一些建議舀凛,從而完善自己的項(xiàng)目和提升自己的技術(shù),這也是一種很好的技術(shù)交流方式途蒋。但是之前一直不知道怎么去寫(xiě)猛遍?怎么去總結(jié)?在經(jīng)過(guò)一些觀摩和學(xué)習(xí)后号坡,終于決定先來(lái)試試水了??懊烤。下面正式開(kāi)始我的第一篇博客。

這篇博客是自己基于iOS系統(tǒng)實(shí)現(xiàn)的指紋解鎖(系統(tǒng)API)和手勢(shì)解鎖(CAShapeLayer)功能宽堆。

在之前自學(xué)CAAnimation腌紧,再加上公司老大說(shuō)可以預(yù)研(之前沒(méi)有做過(guò))一下各種解鎖方式的情況下,想著自己來(lái)實(shí)現(xiàn)一下現(xiàn)在常用的解鎖方式:指紋解鎖手勢(shì)解鎖畜隶。

指紋解鎖

基于iOS的指紋解鎖其實(shí)很簡(jiǎn)單壁肋,因?yàn)橄到y(tǒng)已經(jīng)提供了API給你,你只需要做一些簡(jiǎn)單的判斷和適時(shí)的調(diào)用就可以了籽慢。

第一步

首先導(dǎo)入頭文件#import <LocalAuthentication/LocalAuthentication.h>

判斷是否開(kāi)啟了TouchID浸遗,如果已經(jīng)開(kāi)啟,直接校驗(yàn)指紋箱亿,如果未開(kāi)啟跛锌,則需要先開(kāi)啟TouchID

//判斷是否開(kāi)啟了TouchID
[[[NSUserDefaults standardUserDefaults] objectForKey:@"OpenTouchID"] boolValue]

第二步

  • 未開(kāi)啟TouchID,詢問(wèn)是否開(kāi)啟
- (void)p_openTouchID
{
    dispatch_async(dispatch_get_main_queue(), ^{
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"溫馨提示" message:@"是否開(kāi)啟TouchID?" preferredStyle:UIAlertControllerStyleAlert];
        [alertController addAction:[UIAlertAction actionWithTitle:@"YES" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            //開(kāi)啟TouchID
            [[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:@"OpenTouchID"];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"OpenTouchIDSuccess" object:nil userInfo:nil];
        }]];
        [alertController addAction:[UIAlertAction actionWithTitle:@"NO" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            //不開(kāi)啟TouchID
            [[NSUserDefaults standardUserDefaults] setObject:@(NO) forKey:@"OpenTouchID"];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"OpenTouchIDSuccess" object:nil userInfo:nil];
        }]];
        [self presentViewController:alertController animated:YES completion:nil];
    });
}
  • 已開(kāi)啟TouchID
- (void)p_touchID
{
    dispatch_async(dispatch_get_main_queue(), ^{
        LAContext *context = [[LAContext alloc] init];
        NSError *error = nil;
        //判斷是否支持TouchID
        if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
            [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"TouchID Text" reply:^(BOOL success, NSError * _Nullable error) {
                if (success) {//指紋驗(yàn)證成功
                    [[NSNotificationCenter defaultCenter] postNotificationName:@"UnlockLoginSuccess" object:nil];
                }else {//指紋驗(yàn)證失敗
                    switch (error.code)
                    {
                        case LAErrorAuthenticationFailed:
                        {
                            NSLog(@"授權(quán)失敗"); // -1 連續(xù)三次指紋識(shí)別錯(cuò)誤
                            [[NSNotificationCenter defaultCenter] postNotificationName:@"touchIDFailed" object:nil];
                        }
                            break;
                        case LAErrorUserCancel:
                        {
                            NSLog(@"用戶取消驗(yàn)證Touch ID"); // -2 在TouchID對(duì)話框中點(diǎn)擊了取消按鈕
                            [self dismissViewControllerAnimated:YES completion:nil];
                        }
                            break;
                        case LAErrorUserFallback:
                        {
                            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                                [[NSNotificationCenter defaultCenter] postNotificationName:@"touchIDFailed" object:nil];
                                NSLog(@"用戶選擇輸入密碼届惋,切換主線程處理"); // -3 在TouchID對(duì)話框中點(diǎn)擊了輸入密碼按鈕
                            }];
                            
                        }
                            break;
                        case LAErrorSystemCancel:
                        {
                            NSLog(@"取消授權(quán)髓帽,如其他應(yīng)用切入,用戶自主"); // -4 TouchID對(duì)話框被系統(tǒng)取消脑豹,例如按下Home或者電源鍵
                        }
                            break;
                        case LAErrorPasscodeNotSet:
                            
                        {
                            NSLog(@"設(shè)備系統(tǒng)未設(shè)置密碼"); // -5
                        }
                            break;
                        case LAErrorBiometryNotAvailable:
                        {
                            NSLog(@"設(shè)備未設(shè)置Touch ID"); // -6
                        }
                            break;
                        case LAErrorBiometryNotEnrolled: // Authentication could not start, because Touch ID has no enrolled fingers
                        {
                            NSLog(@"用戶未錄入指紋"); // -7
                        }
                            break;
                            
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
                        case LAErrorBiometryLockout: //Authentication was not successful, because there were too many failed Touch ID attempts and Touch ID is now locked. Passcode is required to unlock Touch ID, e.g. evaluating LAPolicyDeviceOwnerAuthenticationWithBiometrics will ask for passcode as a prerequisite 用戶連續(xù)多次進(jìn)行Touch ID驗(yàn)證失敗氢卡,Touch ID被鎖,需要用戶輸入密碼解鎖晨缴,先Touch ID驗(yàn)證密碼
                        {
                            NSLog(@"Touch ID被鎖译秦,需要用戶輸入密碼解鎖"); // -8 連續(xù)五次指紋識(shí)別錯(cuò)誤,TouchID功能被鎖定击碗,下一次需要輸入系統(tǒng)密碼
                        }
                            break;
                        case LAErrorAppCancel:
                        {
                            NSLog(@"用戶不能控制情況下APP被掛起"); // -9
                        }
                            break;
                        case LAErrorInvalidContext:
                        {
                            NSLog(@"LAContext傳遞給這個(gè)調(diào)用之前已經(jīng)失效"); // -10
                        }
                            break;
#else
#endif
                        default:
                        {
                            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                                NSLog(@"其他情況筑悴,切換主線程處理");
                            }];
                            break;
                        }
                    }
                }
            }];
        }else {
            //不支持
            UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"溫馨提示" message:@"該設(shè)備不支持TouchID" preferredStyle:UIAlertControllerStyleAlert];
            [alertController addAction:[UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleCancel handler:nil]];
            [self presentViewController:alertController animated:YES completion:nil];
        }
    });
}

:代碼中的NSNotificationCenter用于不同操作后的界面跳轉(zhuǎn),重新設(shè)置window.rootViewController,可忽略稍途。

到這里指紋解鎖就結(jié)束了阁吝,很簡(jiǎn)單的一個(gè)API調(diào)用。


手勢(shì)解鎖

其實(shí)在之前還沒(méi)有接觸和剛開(kāi)始接觸iOS開(kāi)發(fā)的時(shí)候械拍,覺(jué)得手勢(shì)解鎖很難突勇,完全不知道怎么去實(shí)現(xiàn)装盯?但是當(dāng)我在自學(xué)CAAnimation的時(shí)候,腦海中突然就想到了一個(gè)實(shí)現(xiàn)手勢(shì)解鎖的方案甲馋,下面就開(kāi)始介紹我的實(shí)現(xiàn)方法:

構(gòu)思

  1. 手勢(shì)解鎖是怎么去驗(yàn)證你滑動(dòng)的手勢(shì)是正確的埂奈?

    其實(shí)手勢(shì)解鎖和輸入密碼的驗(yàn)證是一樣的,在你畫(huà)UI的時(shí)候定躏,你可以給每一個(gè)*圓點(diǎn)*一個(gè)id账磺,在你設(shè)置手勢(shì)的時(shí)候,將滑動(dòng)到對(duì)應(yīng)*圓點(diǎn)*的id放入一個(gè)有序集合中痊远,并保存起來(lái)垮抗,然后驗(yàn)證登錄的時(shí)候,用另外一個(gè)有序集合記錄你當(dāng)前滑動(dòng)到的*圓點(diǎn)*id碧聪,然后和之前保存在本地的進(jìn)行對(duì)比冒版,就可以達(dá)到驗(yàn)證的目的了

  2. 用什么方式去具體實(shí)現(xiàn)UI?

    在之前想過(guò)幾種實(shí)現(xiàn)方式逞姿,但是都被pass掉了壤玫,直到自學(xué)CAAnimation的時(shí)候,才突然意識(shí)到有一個(gè)很好的實(shí)現(xiàn)方式----CAShapeLayer

其實(shí)哼凯,當(dāng)你有了這兩個(gè)問(wèn)題的答案的時(shí)候,你的手勢(shì)解鎖就已經(jīng)實(shí)現(xiàn)了一大部分楚里,后面的部分就是敲代碼了断部。

實(shí)現(xiàn) (工程代碼見(jiàn)文末鏈接)

先上幾張效果圖:(由于本人藝術(shù)細(xì)胞有限,所以為了好看點(diǎn)班缎,界面的UI是參照QQ安全中心的手勢(shì)解鎖)

設(shè)置手勢(shì)

目錄結(jié)構(gòu)

手勢(shì)解鎖目錄結(jié)構(gòu)
  • GesturesViewController:這個(gè)controller用于展示UI蝴光,你可以替換成自己controller,
  • GesturesView:用于圓點(diǎn)按鈕的初始化和布局,
  • PointView圓點(diǎn)手勢(shì)按鈕。

這里主要介紹一下GesturesView和PointView达址,主要邏輯也都在這兩個(gè)類中:

PointView(主要是界面UI蔑祟,不多介紹,直接上代碼)

PointView.h

- (instancetype)initWithFrame:(CGRect)frame
                       withID:(NSString *)ID;

@property (nonatomic, copy, readonly) NSString             *ID;

//選中
@property (nonatomic, assign) BOOL             isSelected;
//解鎖失敗
@property (nonatomic, assign) BOOL             isError;
//解鎖成功
@property (nonatomic, assign) BOOL             isSuccess;
  • -initWithFrame:withID:傳入frameID沉唠,用于初始化PointView疆虚,

  • ID:只讀,用于外部獲取ID满葛,

  • isSelected,isError,isSuccess:用于判斷PointView的狀態(tài)以顯示不同的UI径簿。

PointView.m

通過(guò)懶加載初始化三個(gè)CAShapeLayer

#pragma mark - 懶加載
//外層手勢(shì)按鈕
- (CAShapeLayer *)contentLayer
{
    if (!_contentLayer) {
        _contentLayer = [CAShapeLayer layer];
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(2.0, 2.0, SELF_WIDTH - 4.0, SELF_HEIGHT - 4.0) cornerRadius:(SELF_WIDTH - 4.0) / 2.0];
        _contentLayer.path = path.CGPath;
        _contentLayer.fillColor = RGBCOLOR(46.0, 47.0, 50.0).CGColor;
        _contentLayer.strokeColor = RGBCOLOR(26.0, 27.0, 29.0).CGColor;
        _contentLayer.strokeStart = 0;
        _contentLayer.strokeEnd = 1;
        _contentLayer.lineWidth = 2;
        _contentLayer.cornerRadius = self.bounds.size.width / 2.0;
    }
    return _contentLayer;
}

//手勢(shì)按鈕邊框
- (CAShapeLayer *)borderLayer
{
    if (!_borderLayer) {
        _borderLayer = [CAShapeLayer layer];
        UIBezierPath *borderPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(SELF_WIDTH / 2.0, SELF_HEIGHT / 2.0) radius:SELF_WIDTH / 2.0 startAngle:0 endAngle:2 * M_PI clockwise:NO];
        _borderLayer.strokeColor = RGBCOLOR(105.0, 108.0, 111.0).CGColor;
        _borderLayer.fillColor = [UIColor clearColor].CGColor;
        _borderLayer.strokeEnd = 1;
        _borderLayer.strokeStart = 0;
        _borderLayer.lineWidth = 2;
        _borderLayer.path = borderPath.CGPath;
    }
    return _borderLayer;
}

//選中時(shí),中間樣式
- (CAShapeLayer *)centerLayer
{
    if (!_centerLayer) {
        _centerLayer = [CAShapeLayer layer];
        UIBezierPath *centerPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(SELF_WIDTH / 2.0 - (SELF_WIDTH - 4.0) / 4.0, SELF_HEIGHT / 2.0 - (SELF_HEIGHT - 4.0) / 4.0, (SELF_WIDTH - 4.0) / 2.0, (SELF_WIDTH - 4.0) / 2.0) cornerRadius:(SELF_WIDTH - 4.0) / 4.0];
        _centerLayer.path = centerPath.CGPath;
        _centerLayer.lineWidth = 3;
        _centerLayer.strokeColor = [UIColor colorWithWhite:0 alpha:0.7].CGColor;
        _centerLayer.fillColor = RGBCOLOR(30.0, 180.0, 244.0).CGColor;
    }
    return _centerLayer;
}

設(shè)置PointView的UI狀態(tài)

//根據(jù)情況顯示三種狀態(tài)
- (void)setIsSuccess:(BOOL)isSuccess
{
    _isSuccess = isSuccess;
    if (_isSuccess) {
        self.centerLayer.fillColor = RGBCOLOR(43.0, 210.0, 110.0).CGColor;
    }else {
        self.centerLayer.fillColor = RGBCOLOR(30.0, 180.0, 244.0).CGColor;
    }
}

- (void)setIsSelected:(BOOL)isSelected
{
    _isSelected = isSelected;
    if (_isSelected) {
        self.centerLayer.hidden = NO;
        self.borderLayer.strokeColor = RGBCOLOR(30.0, 180.0, 244.0).CGColor;
    }else {
        self.centerLayer.hidden = YES;
        self.borderLayer.strokeColor = RGBCOLOR(105.0, 108.0, 111.0).CGColor;
    }
}

- (void)setIsError:(BOOL)isError
{
    _isError = isError;
    if (_isError) {
        self.centerLayer.fillColor = RGBCOLOR(222.0, 64.0, 60.0).CGColor;
    }else {
        self.centerLayer.fillColor = RGBCOLOR(30.0, 180.0, 244.0).CGColor;
    }
}

GesturesView(基本所有的邏輯都在這個(gè)里面了)

GesturesView.h

//回傳選擇的id
typedef void (^GestureBlock)(NSArray *selectedID);
//回傳手勢(shì)驗(yàn)證結(jié)果
typedef void (^UnlockBlock)(BOOL isSuccess);
//設(shè)置手勢(shì)失敗
typedef void (^SettingBlock)(void);

@interface GesturesView : UIView

/**
 設(shè)置密碼時(shí)嘀韧,返回設(shè)置的手勢(shì)密碼
 */
@property (nonatomic, copy) GestureBlock             gestureBlock;
                             
/**
 返回解鎖成功還是失敗狀態(tài)
 */
@property (nonatomic, copy) UnlockBlock            unlockBlock;
                             
/**
 判斷手勢(shì)密碼時(shí)候設(shè)置成功(手勢(shì)密碼不得少于四個(gè)點(diǎn))
 */
@property (nonatomic, copy) SettingBlock           settingBlock;

/**
 判斷是設(shè)置手勢(shì)還是手勢(shì)解鎖
 */
@property (nonatomic, assign) BOOL         settingGesture;

這里我申明了三個(gè)block:

  • GestureBlock:將選擇的ID有序集合回傳給控制器篇亭,
  • UnlockBlock:回傳手勢(shì)驗(yàn)證結(jié)果,
  • SettingBlcok:設(shè)置手勢(shì)失敗

屬性:

  • gestureBlock,unlockBlock,settingBlock:分別是對(duì)應(yīng)block的實(shí)例锄贷,
  • settingGesture:用于判斷是設(shè)置手勢(shì)還是手勢(shì)解鎖

GesturesView.h (最主要的邏輯實(shí)現(xiàn)部分)

私有屬性部分:

//可變數(shù)組译蒂,用于存放初始化的點(diǎn)擊按鈕
@property (nonatomic, strong) NSMutableArray             *pointViews;
//記錄手勢(shì)滑動(dòng)的起始點(diǎn)
@property (nonatomic, assign) CGPoint                    startPoint;
//記錄手勢(shì)滑動(dòng)的結(jié)束點(diǎn)
@property (nonatomic, assign) CGPoint                    endPoint;
//存儲(chǔ)選中的按鈕ID
@property (nonatomic, strong) NSMutableArray             *selectedView;
//手勢(shì)滑動(dòng)經(jīng)過(guò)的點(diǎn)的連線
@property (nonatomic, strong) CAShapeLayer               *lineLayer;
//手勢(shì)滑動(dòng)的path
@property (nonatomic, strong) UIBezierPath               *linePath;
//用于存儲(chǔ)選中的按鈕
@property (nonatomic, strong) NSMutableArray             *selectedViewCenter;
//判斷時(shí)候滑動(dòng)是否結(jié)束
@property (nonatomic, assign) BOOL                       touchEnd;

代碼實(shí)現(xiàn)部分:

初始化startPoint曼月,endPoint以及9個(gè)PointView按鈕,startPointendPoint默認(rèn)為0柔昼,并設(shè)置PointViewID

//初始化開(kāi)始點(diǎn)位和結(jié)束點(diǎn)位
    self.startPoint = CGPointZero;
    self.endPoint = CGPointZero;
        //布局手勢(shì)按鈕(采用自定義的全能初始化方法)
    for (int i = 0; i<9 ; i++) {
        PointView *pointView = [[PointView alloc] initWithFrame:CGRectMake((i % 3) * (SELF_WIDTH / 2.0 - 31.0) + 1, (i / 3) * (SELF_HEIGHT / 2.0 - 31.0) + 1, 60, 60)
                                                         withID:[NSString stringWithFormat:@"gestures %d",i + 1]];
        [self addSubview:pointView];
        [self.pointViews addObject:pointView];
    }

滑動(dòng)事件:

  • 開(kāi)始滑動(dòng):

如果self.touchEndYES則直接return哑芹,為NO開(kāi)始以下處理:

  1. 首先獲取到滑動(dòng)的點(diǎn),遍歷所有的PointView岳锁,判斷該點(diǎn)是否在某個(gè)手勢(shì)按鈕范圍,在范圍內(nèi)記錄狀態(tài)绩衷,否則不做處理;
  2. 判斷self.startPoint是否為CGPointZero,如果為YES激率,則將該手勢(shì)按鈕center賦值給self.startPoint;
  3. 判斷該手勢(shì)按鈕的center是否包含在self.selectedViewCenter中咳燕,如果為YES,忽略此次記錄乒躺,為NO則記錄該中心點(diǎn)招盲,用于畫(huà)線,同樣記錄該手勢(shì)按鈕ID嘉冒,用于記錄保存手勢(shì)密碼;
  4. 如果self.startPoint不為CGPointZero曹货,則記錄當(dāng)前滑動(dòng)到的點(diǎn)為self.endPoint熏迹,并劃線模软。
//touch事件
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    if (self.touchEnd) {
        return;
    }
    
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    //判斷手勢(shì)滑動(dòng)是否在手勢(shì)按鈕范圍
    for (PointView *pointView in self.pointViews) {
        //滑動(dòng)到手勢(shì)按鈕范圍,記錄狀態(tài)
        if (CGRectContainsPoint(pointView.frame, point)) {
            //如果開(kāi)始按鈕為zero高每,記錄開(kāi)始按鈕银觅,否則不需要記錄開(kāi)始按鈕
            if (CGPointEqualToPoint(self.startPoint, CGPointZero)) {
                self.startPoint = pointView.center;
            }
            //判斷該手勢(shì)按鈕的中心點(diǎn)是否記錄礼饱,未記錄則記錄
            if (![self.selectedViewCenter containsObject:[NSValue valueWithCGPoint:pointView.center]]) {
                [self.selectedViewCenter addObject:[NSValue valueWithCGPoint:pointView.center]];
            }
            //判斷該手勢(shì)按鈕是否已經(jīng)選中,未選中就選中
            if (![self.selectedView containsObject:pointView.ID]) {
                [self.selectedView addObject:pointView.ID];
                pointView.isSelected = YES;
            }
        }
    }
    //如果開(kāi)始點(diǎn)位不為zero則記錄結(jié)束點(diǎn)位究驴,否則跳過(guò)不記錄
    if (!CGPointEqualToPoint(self.startPoint, CGPointZero)) {
        self.endPoint = point;
        [self p_drawLines];
    }
}
  • 畫(huà)線:

如果self.touchEndYES則直接return镊绪,為NO開(kāi)始畫(huà)線:

  1. 首先移除self.lineLayer,self.linePath,否則你會(huì)發(fā)現(xiàn)隨著你的滑動(dòng),會(huì)出現(xiàn)很多條線洒忧。蝴韭;
  2. 設(shè)置self.linePath的起始點(diǎn),并遍歷self.selectedViewCenter,為self.linePath添加節(jié)點(diǎn)熙侍,最后將self.endPoint添加上去(為結(jié)束滑動(dòng)的時(shí)候榄鉴,self.endPoint為當(dāng)前滑動(dòng)位置的點(diǎn));
  3. 設(shè)置self.lineLayer的相應(yīng)屬性蛉抓,并添加到self.layer牢硅。
//畫(huà)線
- (void)p_drawLines
{
    //結(jié)束手勢(shì)滑動(dòng),不畫(huà)線
    if (self.touchEnd) {
        return;
    }
    //移除path的點(diǎn)和lineLayer
    [self.lineLayer removeFromSuperlayer];
    [self.linePath removeAllPoints];
    //畫(huà)線
    [self.linePath moveToPoint:self.startPoint];
    for (NSValue *pointValue in self.selectedViewCenter) {
        [self.linePath addLineToPoint:[pointValue CGPointValue]];
    }
    [self.linePath addLineToPoint:self.endPoint];
    
    self.lineLayer.path = self.linePath.CGPath;
    self.lineLayer.lineWidth = 4.0;
    self.lineLayer.strokeColor = RGBCOLOR(30.0, 180.0, 244.0).CGColor;
    self.lineLayer.fillColor = [UIColor clearColor].CGColor;
    
    [self.layer addSublayer:self.lineLayer];
    
    self.layer.masksToBounds = YES;
}
  • 結(jié)束滑動(dòng):
  1. self.endPoint設(shè)置為self.selectedViewCenter.lastObject芝雪,如果self.endPoint還是為CGPointZero减余,則說(shuō)明未滑動(dòng)到手勢(shì)按鈕范圍,不做任何處理惩系,否則繼續(xù)以下邏輯處理位岔;
  2. 再次調(diào)用-(void)p_drawLines畫(huà)線如筛;
  3. 判斷是設(shè)置手勢(shì)密碼還是手勢(shì)解鎖
    1. 設(shè)置手勢(shì)密碼
      1. 如果選中的手勢(shì)按鈕數(shù)量少于4抒抬,設(shè)置self.touchEnd = NO使其可以重新設(shè)置杨刨,return結(jié)束此次設(shè)置;
      2. 如果設(shè)置的手勢(shì)按鈕符合要求則調(diào)用self.gestureBlock(self.selectedView)將手勢(shì)密碼回傳給控制器擦剑;
    2. 手勢(shì)解鎖
      1. 獲取本地存儲(chǔ)的手勢(shì)密碼妖胀;我這里用的是NSUserDefaults,其實(shí)這是不安全的惠勒,建議使用Keychain赚抡,我也會(huì)在后續(xù)的更新中使用Keychain 已使用keychain保存密碼,具體使用見(jiàn)Demo
      2. 如果self.selectedView和本地手勢(shì)密碼一樣,則解鎖成功纠屋,并設(shè)置pointView.isSuccess = YES改變手勢(shì)按鈕樣式等涂臣,并調(diào)用self.unlockBlock(YES),告知控制器結(jié)果售担;
      3. 否則解鎖失敗赁遗,pointView.isError = YES改變手勢(shì)按鈕樣式等,并調(diào)用self.unlockBlock(NO),告知控制器結(jié)果族铆;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //結(jié)束手勢(shì)滑動(dòng)的時(shí)候岩四,將結(jié)束按鈕設(shè)置為最后一個(gè)手勢(shì)按鈕的中心點(diǎn),并畫(huà)線
    self.endPoint = [self.selectedViewCenter.lastObject CGPointValue];
    //如果endPoint還是為zero說(shuō)明未滑動(dòng)到有效位置哥攘,不做處理
    if (CGPointEqualToPoint(self.endPoint, CGPointZero)) {
        return;
    }
    [self p_drawLines];
    //改變手勢(shì)滑動(dòng)結(jié)束的狀態(tài)剖煌,為yes則無(wú)法在滑動(dòng)手勢(shì)劃線
    self.touchEnd = YES;
    //設(shè)置手勢(shì)時(shí),返回設(shè)置的時(shí)候密碼献丑,否則繼續(xù)下面的操作進(jìn)行手勢(shì)解鎖
    if (_gestureBlock && _settingGesture) {
        //手勢(shì)密碼不得小于4個(gè)點(diǎn)
        if (self.selectedView.count < 4) {
            self.touchEnd = NO;
            for (PointView *pointView in self.pointViews) {
                pointView.isSelected = NO;
            }
            [self.lineLayer removeFromSuperlayer];
            [self.selectedView removeAllObjects];
            self.startPoint = CGPointZero;
            self.endPoint = CGPointZero;
            [self.selectedViewCenter removeAllObjects];
            if (_settingBlock) {
                self.settingBlock();
            }
            return;
        }
        _gestureBlock(self.selectedView);
        return;
    }
    
    //手勢(shì)解鎖
    NSArray *selectedID = [[NSUserDefaults standardUserDefaults] objectForKey:@"GestureUnlock"];
    //解鎖成功
    if ([self.selectedView isEqualToArray:selectedID]) {
        //解鎖成功,遍歷pointview侠姑,設(shè)置為成功狀態(tài)
        for (PointView *pointView in self.pointViews) {
            pointView.isSuccess = YES;
        }
        self.lineLayer.strokeColor = RGBCOLOR(43.0, 210.0, 110.0).CGColor;
        if (_unlockBlock) {
            self.unlockBlock(YES);
        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:@"UnlockLoginSuccess" object:nil];
        });
    }else {//解鎖失敗
        //解鎖失敗创橄,遍歷pointView,設(shè)置為失敗狀態(tài)
        for (PointView *pointView in self.pointViews) {
            pointView.isError = YES;
        }
        self.lineLayer.strokeColor = RGBCOLOR(222.0, 64.0, 60.0).CGColor;
        if (_unlockBlock) {
            self.unlockBlock(NO);
        }
    }
}

到這里就實(shí)現(xiàn)了手勢(shì)解鎖的所有邏輯莽红,在實(shí)現(xiàn)之前還在擔(dān)心有什么問(wèn)題妥畏,結(jié)果實(shí)現(xiàn)出來(lái)之后感覺(jué)其實(shí)很簡(jiǎn)單。


最后

希望這篇文章能夠幫助到一些人安吁。對(duì)于代碼博客的一些規(guī)范希望大家諒解一下了醉蚁,后面也會(huì)慢慢去優(yōu)化的。最后的最后附上Demo的鏈接 Demo-GitHub鬼店。

手勢(shì)解鎖.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末网棍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子妇智,更是在濱河造成了極大的恐慌滥玷,老刑警劉巖氏身,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異惑畴,居然都是意外死亡蛋欣,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)如贷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)陷虎,“玉大人,你說(shuō)我怎么就攤上這事杠袱∩性常” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵霞掺,是天一觀的道長(zhǎng)谊路。 經(jīng)常有香客問(wèn)我,道長(zhǎng)菩彬,這世上最難降的妖魔是什么缠劝? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮骗灶,結(jié)果婚禮上惨恭,老公的妹妹穿的比我還像新娘。我一直安慰自己耙旦,他們只是感情好脱羡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著免都,像睡著了一般锉罐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绕娘,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天脓规,我揣著相機(jī)與錄音,去河邊找鬼险领。 笑死侨舆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绢陌。 我是一名探鬼主播挨下,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼脐湾!你這毒婦竟也來(lái)了臭笆?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耗啦,沒(méi)想到半個(gè)月后凿菩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡帜讲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年衅谷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片似将。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡获黔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出在验,到底是詐尸還是另有隱情玷氏,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布腋舌,位于F島的核電站盏触,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏块饺。R本人自食惡果不足惜赞辩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望授艰。 院中可真熱鬧辨嗽,春花似錦、人聲如沸淮腾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谷朝。三九已至洲押,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間圆凰,已是汗流浹背杈帐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留送朱,地道東北人娘荡。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓干旁,卻偏偏與公主長(zhǎng)得像驶沼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子争群,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容