Based on the TouchID fingerprint unlock technology, and now practice a way to unlock: nine grid gesture unlock.

In some scenarios involving personal privacy, it is extremely necessary to consider the safety of users.

First of all, we first analyze the implementation process of the function. First of all, we need to look at the general implementation process: 1. 2. The realization of the button of the nine grid and the change of the button state in the process of being clicked and sliding, so as to create the path, realize the line in the process of sliding, and draw the graph 3. 4. Add the defined 9 grid View and 9 grid indicator View to gesture password interface controller 5. The corresponding operation of gesture password is realized through gesture enumeration.

####1.1 Distribution of internal control parts 3×3, we can customize the view (including 3×3 buttons), the selected picture and the normal default button picture can be replaced.

#pragma mark - initializer

- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    
    if (self) {
        
        [self initSubviews];
    }
    returnself; } // Initialize subviews - (void)initSubviews {self.backgroundColor = [UIColor clearColor]; UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; [self addGestureRecognizer:pan]; // Create 9 buttonsfor (NSInteger i = 0; i < 9; i++) {
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
        btn.userInteractionEnabled = NO;
        [btn setImage:[UIImage imageNamed:@"gesture_normal"] forState:UIControlStateNormal];
        [btn setImage:[UIImage imageNamed:@"gesture_selected"] forState:UIControlStateSelected]; [self addSubview:btn]; btn.tag = i + 1; Void layoutSubviews {[super layoutSubviews]; void layoutSubviews {[super layoutSubviews]; NSUInteger count = self.subviews.count; int cols = 3; // CGFloat x = 0,y = 0,w = 0,h = 0;if (Screen_Width == 320) {
        w = 50;
        h = 50;
    } else{ w = 58; h = 58; } CGFloat margin = (self.bounds.size.width - cols * w) / (cols + 1); // Spacing CGFloat col = 0; CGFloat row = 0;for (int i = 0; i < count; i++) {
        
        col = i % cols;
        row = i / cols;
        
        x = margin + (w+margin)*col;
        
        y = margin + (w+margin)*row;
        if(Screen_Height == 480) {y = (w + margin) * row; }else{ y = margin +(w + margin) * row; } UIButton *btn = self.subviews[i]; btn.frame = CGRectMake(x, y, w, h); }}Copy the code

####1.2 Define a display nine grid method ** Note: ** We determine the gesture password through string splice according to the button tag value defined in the previous layout of the button, password transfer value is realized by proxy.

@protocol ZLGestureLockDelegate <NSObject>

- (void)gestureLockView:(ZLGestureLockView *)lockView drawRectFinished:(NSMutableString *)gesturePassword;

@end

@interface ZLGestureLockView : UIView

@property (assign, nonatomic) id<ZLGestureLockDelegate> delegate;

@end
Copy the code

####2.1 Defines the member properties of the array type, used to install the button that is clicked

@property (strong, nonatomic) NSMutableArray *selectBtns;
Copy the code
#pragma mark - getter

- (NSMutableArray *)selectBtns {
    if(! _selectBtns) { _selectBtns = [NSMutableArray array]; }return _selectBtns;
}
Copy the code

####2.2 Create a path to realize the line in the sliding process and draw a graph.

Void drawRect:(CGRect)rect {void drawRect:(CGRect)rect {if (_selectBtns.count == 0) return; UIBezierPath *path = [UIBezierPath bezierPath];if (self.userInteractionEnabled) {
        [[UIColor yellowColor] set];
    } else {
        [[UIColor orangeColor] set];
    }
    for (int i = 0; i < self.selectBtns.count; i ++) {
        UIButton *btn = self.selectBtns[i];
        if(i == 0) { [path moveToPoint:btn.center]; // Set the starting point}else {
            [path addLineToPoint:btn.center];
        }
    }
    [path addLineToPoint:_currentPoint];
    
    [UIColorFromRGB(0xffc8ad) set];
    path.lineWidth = 6;
    path.lineJoinStyle = kCGLineCapRound;
    path.lineCapStyle = kCGLineCapRound;
    [path stroke];
}
Copy the code

####2.3 Start touch, save the input password and call back after the gesture password is drawn

#pragma mark - action pan

- (void)pan:(UIPanGestureRecognizer *)pan {
    _currentPoint = [pan locationInView:self];
    
    [self setNeedsDisplay];
    
    for (UIButton *button in self.subviews) {
        if (CGRectContainsPoint(button.frame, _currentPoint) && button.selected ==   NO) {
            
            button.selected = YES;
            [self.selectBtns addObject:button];
        }
    }
    
    [self layoutIfNeeded];
    
    if(pan) state = = UIGestureRecognizerStateEnded) {/ / save the input password NSMutableString * gesturePwd = @"".mutableCopy;
        for (UIButton *button in self.selectBtns) {
            [gesturePwd appendFormat:@"%ld",button.tag-1]; button.selected = NO; } [self.selectBtns removeAllObjects]; // Call back after the gesture password is drawnif([self.delegate respondsToSelector:@selector(gestureLockView:drawRectFinished:)]) { [self.delegate gestureLockView:self drawRectFinished:gesturePwd]; }}}Copy the code

ZLGestureLockIndicator ####3.1 The distribution of the internal control is also 3×3, we can also customize the view (including 3×3 buttons)

#pragma mark - initializer

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self initSubviews];
    }
    returnself; } // Subview initialization - (void)initSubviews {// Create 9 buttonsfor (int i = 0; i < 9; i++) {
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
        btn.userInteractionEnabled = NO;
        [btn setImage:[UIImage imageNamed:@"gesture_indicator_normal"] forState:UIControlStateNormal];
        [btn setImage:[UIImage imageNamed:@"gesture_indicator_selected"] forState:UIControlStateSelected]; [self addSubview:btn]; [self.btns addObject:btn]; } } - (void)layoutSubviews { [super layoutSubviews]; NSUInteger count = self.subviews.count; int cols = 3; // CGFloat x = 0,y = 0,w = 9,h = 9; //bounds CGFloat margin = (self.bounds.size.width - cols * w) / (cols + 1); // Spacing CGFloat col = 0; CGFloat row = 0;for(int i = 0; i < count; i++) { col = i%cols; row = i/cols; x = margin + (w+margin)*col; y = margin + (w+margin)*row; UIButton *btn = self.subviews[i]; btn.frame = CGRectMake(x, y, w, h); }}Copy the code

####3.2 Define the member properties of array types to hold the clicked button in the grid (and zoom out to show the selected grid)

@property (nonatomic, strong) NSMutableArray *btns;
Copy the code
#pragma mark - getter

- (NSMutableArray *)btns {
    if(! _btns) { _btns = [NSMutableArray array]; }return _btns;
}
Copy the code

####3.3 Path of the command output

- (void)setGesturePassword:(NSString *)gesturePassword;
Copy the code
#pragma mark - public
- (void)setGesturePassword:(NSString *)gesturePassword {
    
    if (gesturePassword.length == 0) {
        for (UIButton *button in self.btns) {
            button.selected = NO;
        }
        return;
    }
    
    for (int i = 0; i < gesturePassword.length; i++) {
        
        NSString *s = [gesturePassword substringWithRange:NSMakeRange(i, 1)];
        
        [self.btns[s.integerValue] setSelected:YES]; }}Copy the code

# # 4 will be defined scratchable latex view and scratchable latex indicator view added to the gestures ZLGestureLockViewController password interface controller

####4.1 Loading data to the controller

// ZLGestureLockIndicator *gestureLockIndicator = [[ZLGestureLockIndicator Alloc] initWithFrame: CGRectMake (self. View. Frame. The size, width - 60) * 0.5, 110, 60, 60)]; [self.view addSubview:gestureLockIndicator]; self.gestureLockIndicator = gestureLockIndicator; [[ZLGestureLockView alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height - self.view.frame.size.width - 60 - btnH, self.view.frame.size.width, self.view.frame.size.width)]; gestureLockView.delegate = self; [self.view addSubview:gestureLockView]; self.gestureLockView = gestureLockView;Copy the code

####4.2 Creating an enumeration of gesture passwords

#import <UIKit/UIKit.h>Typedef NS_ENUM(NSInteger,ZLUnlockType) {ZLUnlockTypeCreatePsw, // Create gesture password ZLUnlockTypeValidatePsw // verify gesture password}; @interface ZLGestureLockViewController : UIViewController + (void)deleteGesturesPassword; // Delete gesture password + (NSString *)gesturesPassword; - (instancetype)initWithUnlockType:(ZLUnlockType)unlockType; @endCopy the code
@property (nonatomic, copy) NSString *lastGesturePsw; @property (nonatomic) ZLUnlockType unlockType;Copy the code
#pragma mark - inint

- (instancetype)initWithUnlockType:(ZLUnlockType)unlockType {
    if (self = [super init]) {
        _unlockType = unlockType;
    }
    return self;
}

#pragma mark - viewDidLoad
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    [self setupMainUI];
    
    self.gestureLockView.delegate = self;
    
    self.resetPswBtn.hidden = YES;
    switch (_unlockType) {
        case ZLUnlockTypeCreatePsw:
        {
            self.gestureLockIndicator.hidden = NO;
            self.otherAcountBtn.hidden = self.forgetPswBtn.hidden = self.nameLabel.hidden = self.headIcon.hidden = YES;
        }
            break;
        case ZLUnlockTypeValidatePsw:
        {
            self.gestureLockIndicator.hidden = YES;
            self.otherAcountBtn.hidden = self.forgetPswBtn.hidden = self.nameLabel.hidden = self.headIcon.hidden = NO;
            
        }
            break;
        default:
            break; }}Copy the code
#pragma mark - ZLgestureLockViewDelegate

- (void)gestureLockView:(ZLGestureLockView *)lockView drawRectFinished:(NSMutableString *)gesturePassword {
    
    switch (_unlockType) {
        caseZLUnlockTypeCreatePsw: / / create gestures password {[self createGesturesPassword: gesturePassword]; }break;
        caseZLUnlockTypeValidatePsw: / / password check gestures {[self validateGesturesPassword: gesturePassword]; }break;
        default:
            break; }}Copy the code

####4.3 Creating and saving a Gesture Password

#pragma mark - Class method

+ (void)deleteGesturesPassword {
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:GesturesPassword];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

+ (void)addGesturesPassword:(NSString *)gesturesPassword {
    [[NSUserDefaults standardUserDefaults] setObject:gesturesPassword forKey:GesturesPassword];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

+ (NSString *)gesturesPassword {
    return [[NSUserDefaults standardUserDefaults] objectForKey:GesturesPassword];
}
Copy the code
#pragma mark - private- (void)createGesturesPassword:(NSMutableString *)gesturesPassword {if (self.lastGesturePsw.length == 0) {
        
        if (gesturesPassword.length < 4) {
            self.statusLabel.text = @"Connect at least four points, please re-enter";
            [self shakeAnimationForView:self.statusLabel];
            return;
        }
        
        if (self.resetPswBtn.hidden == YES) {
            self.resetPswBtn.hidden = NO;
        }
        
        self.lastGesturePsw = gesturesPassword;
        [self.gestureLockIndicator setGesturePassword:gesturesPassword];
        self.statusLabel.text = @"Please draw the gesture password again.";
        return;
    }
    
    if([self lastGesturePsw isEqualToString: gesturesPassword]) {/ / drawing successful [self dismissViewControllerAnimated: YES completion: ^ { / / save gestures password [ZLGestureLockViewController addGesturesPassword: gesturesPassword];}]; }else {
        self.statusLabel.text = @"Inconsistent with last drawing, please redraw"; [self shakeAnimationForView:self.statusLabel]; }}Copy the code

####4.4 Verifying the gesture password and the logic of incorrect password input vary from product to product. In this case, I can enter incorrect password for five times and shake the password. Otherwise, I can log out and log in again.

/ / password authentication gestures - (void) validateGesturesPassword: (NSMutableString *) gesturesPassword {static NSInteger errorCount = 5;if ([gesturesPassword isEqualToString:[ZLGestureLockViewController gesturesPassword]]) {
        
        [self dismissViewControllerAnimated:YES completion:^{
            errorCount = 5;
        }];
    } else {
        
        if(errorCount -1 == 0) {// You have entered the error five times! Exit and log back in! UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Gesture password is no longer valid." message:@"Please log in again." delegate:self cancelButtonTitle:nil otherButtonTitles:@"Re-landing", nil];
            [alertView show];
            errorCount = 5;
            return;
        }
        
        self.statusLabel.text = [NSString stringWithFormat:@"Wrong password, you can enter %ld again",--errorCount]; [self shakeAnimationForView:self.statusLabel]; }}Copy the code
// shake animation - (void)shakeAnimationForView:(UIView *)view {CALayer *viewLayer = view.layer; CGPoint position = viewLayer.position; CGPoint left = CGPointMake(position.x - 10, position.y); CGPoint right = CGPointMake(position.x + 10, position.y); CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
    [animation setFromValue:[NSValue valueWithCGPoint:left]];
    [animation setToValue:[NSValue valueWithCGPoint:right]];
    [animation setAutoreverses:YES]; // Finish smooth [animation]setDuration: 0.08]; [animationsetRepeatCount:3];
    
    [viewLayer addAnimation:animation forKey:nil];
}
Copy the code
#pragma mark - UIAlertViewDelegate- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {// re-log in to NSLog(@)"Re-landing");
}
Copy the code

####4.5 Other button events at the bottom, especially the redraw button, remember to leave the created gesture password blank

#pragma Mark - Button click event Anction- (void)otherAccountLogin:(id)sender {NSLog(@)"%s",__FUNCTION__); } // click the redraw button - (void)resetGesturePassword:(id)sender {NSLog(@)"%s",__FUNCTION__);
    
    self.lastGesturePsw = nil;
    self.statusLabel.text = @"Please draw a gesture password.";
    self.resetPswBtn.hidden = YES;
    [self.gestureLockIndicator setGesturePassword:@""]; } // click forget gesture password button - (void)forgetGesturesPassword:(id)sender {NSLog(@)"%s",__FUNCTION__);
}
Copy the code

##5 Use gesture enumeration to realize the corresponding operation of gesture password

/ / create gestures password ZLGestureLockViewController * vc = [[ZLGestureLockViewController alloc] initWithUnlockType:ZLUnlockTypeCreatePsw]; [self presentViewController:vc animated:YES completion:nil];Copy the code
// Verify the gesture passwordif ([ZLGestureLockViewController gesturesPassword].length > 0) {
            
     ZLGestureLockViewController *vc = [[ZLGestureLockViewController alloc] initWithUnlockType:ZLUnlockTypeValidatePsw];
     [self presentViewController:vc animated:YES completion:nil];
 } else {
      UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:@"No gesture password has been set." delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
       [alertView show];
 }
Copy the code
/ / remove gestures password [ZLGestureLockViewController deleteGesturesPassword];Copy the code

OK, now you can see the effect!