iOS 开发实战-锁屏界面(手势解锁)

之前写了一篇关于锁屏的文章,是密码锁屏,可以参照:

该篇是要讲解如何开发手势解锁,完整代码在github上。

关于如何在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。


如果有任何问题欢迎再下面留言,或者扫描二维码

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是手势的Demo实现过程: 1. 首先创建一个UIView的子类,作为手势的主体视图,我们称之为`GestureLockView`。 2. 在`GestureLockView`中创建一个数组`circleArray`,用于存储手势的圆点。 ``` @property (nonatomic, strong) NSMutableArray *circleArray; ``` 3. 在`GestureLockView`的`layoutSubviews`方法中,创建9个圆点,并加入到`circleArray`中。 ``` - (void)layoutSubviews { [super layoutSubviews]; CGFloat margin = (self.frame.size.width - 3 * kCircleSize) / 4.0; for (int i = 0; i < 9; i++) { CGFloat x = margin + (i % 3) * (margin + kCircleSize); CGFloat y = margin + (i / 3) * (margin + kCircleSize); CGRect frame = CGRectMake(x, y, kCircleSize, kCircleSize); GestureLockCircle *circle = [[GestureLockCircle alloc] initWithFrame:frame]; circle.tag = i + 1; [self addSubview:circle]; [self.circleArray addObject:circle]; } } ``` 4. 在`GestureLockView`中创建一个数组`selectedArray`,用于存储用户选择的圆点。 ``` @property (nonatomic, strong) NSMutableArray *selectedArray; ``` 5. 在`GestureLockView`中实现手势识别的方法`touchesMoved:withEvent:`,通过判断触摸点是否在圆点内来确定用户选择的圆点,并绘制用户选择的线条。 ``` - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self]; for (GestureLockCircle *circle in self.circleArray) { if (CGRectContainsPoint(circle.frame, point) && !circle.selected) { circle.selected = YES; [self.selectedArray addObject:circle]; break; } } self.currentPoint = point; [self setNeedsDisplay]; } ``` 6. 在`GestureLockView`中实现绘制方法`drawRect:`,根据用户选择的圆点绘制线条。 ``` - (void)drawRect:(CGRect)rect { if (self.selectedArray.count == 0) { return; } UIBezierPath *path = [UIBezierPath bezierPath]; path.lineWidth = kLineWidth; path.lineJoinStyle = kCGLineJoinRound; path.lineCapStyle = kCGLineCapRound; [[UIColor whiteColor] set]; for (int i = 0; i < self.selectedArray.count; i++) { GestureLockCircle *circle = self.selectedArray[i]; if (i == 0) { [path moveToPoint:circle.center]; } else { [path addLineToPoint:circle.center]; } } [path addLineToPoint:self.currentPoint]; [path stroke]; } ``` 7. 在`GestureLockView`中实现手势结束的方法`touchesEnded:withEvent:`,判断用户手势是否正确,并通过代理方法通知外部。 ``` - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSMutableString *password = [NSMutableString string]; for (GestureLockCircle *circle in self.selectedArray) { [password appendFormat:@"%ld", circle.tag]; } BOOL success = [password isEqualToString:self.password]; if (success) { for (GestureLockCircle *circle in self.selectedArray) { circle.selected = NO; } [self.selectedArray removeAllObjects]; [self setNeedsDisplay]; if (self.delegate && [self.delegate respondsToSelector:@selector(gestureLockView:didCompleteWithPassword:)]) { [self.delegate gestureLockView:self didCompleteWithPassword:password]; } } else { for (GestureLockCircle *circle in self.selectedArray) { circle.selected = NO; circle.error = YES; } [self.selectedArray removeAllObjects]; [self setNeedsDisplay]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kErrorDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ for (GestureLockCircle *circle in self.circleArray) { circle.error = NO; } [self setNeedsDisplay]; }); } } ``` 8. 在外部创建`GestureLockView`实例,并设置代理方法,实现手势的逻辑。 ``` - (void)viewDidLoad { [super viewDidLoad]; GestureLockView *lockView = [[GestureLockView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenWidth)]; lockView.center = self.view.center; lockView.delegate = self; lockView.password = @"123456789"; [self.view addSubview:lockView]; } #pragma mark - GestureLockViewDelegate - (void)gestureLockView:(GestureLockView *)lockView didCompleteWithPassword:(NSString *)password { NSLog(@"password: %@", password); } ``` 至此,手势的Demo已经完成了,你可以尝试在模拟器或真机上运行它。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值