1. 程式人生 > >iOS 開發實戰-鎖屏介面(手勢解鎖)

iOS 開發實戰-鎖屏介面(手勢解鎖)

之前寫了一篇關於鎖屏的文章,是密碼鎖屏,可以參照:

關於如何在App後臺啟動等問題,該篇就不再贅述,之專注於介紹核心的實現部分。原始碼在Github上可以獲取。

實現思路

手勢鎖屏是一個3*3的9宮格介面,將每一個宮格用一個Button表示,然後給每一個button附上一個tag,捕捉touch事件,通過判斷手勢劃過哪些button,紀錄下tag數值,作為密碼。

變數定義

手勢鎖屏是定義在GuestureView的UIView類中。主要的UI和事件響應都在這個類中完成。

[code]

@interface GuestureView()
@property (nonatomic,strong) NSMutableArray * buttonsArray;
@property (nonatomic,assign) CGPoint currentPoi;

定義兩個變數:

buttonsArray:用來記錄劃過哪些Button

currentPoi用:來記錄當前手指所在的point

UI佈局

定義9個UIButton控制元件

[code]

-(id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self configButtons];
    }
    return self;
}

-(void)configButtons
{
    
    self.buttonsArray = [NSMutableArray array];
    for (int i = 0 ; i < 9 ; ++i) {
        UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
        btn.tag = i;
        btn.userInteractionEnabled = NO;
        [self addSubview:btn];
        [btn setBackgroundImage:[UIImage imageNamed:@"lock"] forState:UIControlStateNormal];
        [btn setBackgroundImage:[UIImage imageNamed:@"unlock"] forState:UIControlStateSelected];
    }
}

-(void)layoutSubviews
{
    [super layoutSubviews];
    for(int i = 0 ; i < [self.subviews count] ; ++i) {
        UIButton *btn=self.subviews[i];
        CGFloat row = i/3;
        CGFloat loc   = i%3;
        CGFloat btnW=74;
        CGFloat btnH=74;
        CGFloat padding=(self.frame.size.width-3*btnW)/4;
        CGFloat btnX=padding+(btnW+padding)*loc;
        CGFloat btnY=padding+(btnW+padding)*row;
        btn.frame=CGRectMake(btnX, btnY, btnW, btnH);
    }
    
    //密碼提示Label
    UILabel * passwordLabel = [[UILabel alloc] init];
    passwordLabel.text = @"密碼是:L";
    passwordLabel.textColor = [UIColor grayColor];
    [passwordLabel sizeToFit];
    [self addSubview:passwordLabel];
    passwordLabel.translatesAutoresizingMaskIntoConstraints = NO;
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-400-[passwordLabel]"
                                                                 options:0
                                                                 metrics:nil
                                                                   views:NSDictionaryOfVariableBindings(passwordLabel)]];
}

每個Button都賦給一個tag,且userInteractionEnabled為NO。

Button的frame在layoutSubviews裡設定。

手勢響應

主要實現三個touch事件:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

 -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;

 -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

這三個事件是手勢解鎖的核心程式碼。

[code]

#pragma mark - touch event
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGPoint startPoint = [self getCurrentPoint:touches];
    UIButton * btn = [self getButtonWithCurrentPoint:startPoint];
    if (btn && btn.selected != YES) {
        btn.selected = YES;
        [self.buttonsArray addObject:btn];
    }
    self.currentPoi = startPoint;
    [self setNeedsDisplay];
    
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGPoint point = [self getCurrentPoint:touches];
    UIButton * btn = [self getButtonWithCurrentPoint:point];
    if (btn && btn.selected != YES) {
        btn.selected = YES;
        [self.buttonsArray addObject:btn];
    }
    [self setNeedsDisplay];
    self.currentPoi = point;
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSMutableString * passWordString = [[NSMutableString alloc] init];
    for (UIButton * btn in self.buttonsArray) {
        [passWordString appendFormat:@"%ld", (long)btn.tag];
    }
    NSLog(@"password is %@",passWordString);
    
    [self.buttonsArray makeObjectsPerformSelector:@selector(setSelected:) withObject:@NO];
    [self.buttonsArray removeAllObjects];
    [self setNeedsDisplay];
    self.currentPoi = CGPointZero;
    
    if ([self.delegate respondsToSelector:@selector(unlockFromGuesture:)]) {
        if ([passWordString isEqualToString:@"03678"])
        {
            [self.delegate unlockFromGuesture:YES];
        }
        else
        {
            [self.delegate unlockFromGuesture:NO];
        }

    }
}

其中touchedEnded中呼叫了一個delegate方法,這個方法是為通知superView密碼是否正確,然後交給superview來處理。

程式碼中涉及到兩個方法:

//獲得當前手指所在point
-(CGPoint)getCurrentPoint:(NSSet *)touches
//獲得該點所在的Button。
-(UIButton *)getButtonWithCurrentPoint:(CGPoint)point

[code]

#pragma mark - point event
-(CGPoint)getCurrentPoint:(NSSet *)touches
{
    UITouch * touch = [touches anyObject];
    CGPoint point = [touch locationInView:touch.view];
    return point;
}

-(UIButton *)getButtonWithCurrentPoint:(CGPoint)point
{
    for (UIButton * btn in self.subviews) {
        if (CGRectContainsPoint(btn.frame, point)) {
            return btn;
        }
    }
    return nil;
}

繪圖

用Core Graphic實現劃線效果。

[code]

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClearRect(context, rect);
    
    for (int i = 0; i < self.buttonsArray.count; ++i) {
        UIButton * btn = self.buttonsArray[i];
        if (0 == i)
        {
            CGContextMoveToPoint(context, btn.center.x, btn.center.y);
        }
        else
        {
            CGContextAddLineToPoint(context, btn.center.x,btn.center.y);
        }
    }
    
    if (self.buttonsArray.count > 0) {
        CGContextAddLineToPoint(context, self.currentPoi.x, self.currentPoi.y);
    }
    
    CGContextSetLineWidth(context, 10);
    CGContextSetLineJoin(context, kCGLineJoinRound);
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetRGBStrokeColor(context, 20/255.0, 107/255.0, 153/255.0, 1);
    CGContextStrokePath(context);
    CGContextSaveGState(context);
    CGContextRestoreGState(context);
}
該實現放在drawRect方法中,每當呼叫setNeedsDisplay方法是都會執行drawRect。 如果有任何問題歡迎再下面留言,或者掃描二維碼