iOS Quartz2D实战系列-手把手教你手势解锁

手势解锁

这是我模仿的QQ手势解锁部分界面~


密码输入错误效果


密码输入正确效果


界面粗糙,还请海涵~


接下来开始上代码了~

我是自定义了一个SpeedDailView继承于UIView的九宫格界面

解释都在注释里了

SpeedDialView.h

#import <UIKit/UIKit.h>

@interface SpeedDialView : UIView
//用来盛放选中的button
@property (nonatomic,retain) NSMutableArray *btns;
//输入的密码
@property (nonatomic,strong) NSMutableString *code;
//正确密码
@property (nonatomic,strong) NSString *password;
//触摸是否结束
@property (nonatomic,assign) BOOL isEnd;
@end
SpeedDialView.m

#import "SpeedDialView.h"

@implementation SpeedDialView
- (instancetype)init
{
    self = [super init];
    if (self) {
    }
    return self;
}
//懒加载
- (NSMutableArray *)btns
{
    if (!_btns) {
        self.btns = [[NSMutableArray alloc]init];
    }
    return _btns;
}
//创建9个按钮
- (void)setUpBasicUI
{
    for (int i = 0; i < 9; i++) {
        UIButton *btn = [UIButton buttonWithType:(UIButtonTypeCustom)];
        btn.tag = i;
        [self addSubview:btn];
    }
}
//设置9个按钮的位置
- (void)layoutSubviews
{
    [super layoutSubviews];
    //这个方法只运行一次 
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self setUpBasicUI];
        
    });
    //经典的9宫格算法
    int totalColume = 3;
    CGFloat bWidth = 74;
    CGFloat bHeight = bWidth;
    CGFloat margin = (self.frame.size.width - bWidth * totalColume)/(totalColume + 1);
    
    for (int i = 0; i< self.subviews.count; i++) {
        int currentRow = i/totalColume;
        int currentColumn = i%totalColume;
        CGFloat originX = margin + (currentColumn * (margin + bWidth));
        CGFloat originY = currentRow * (margin + bHeight);
        //设置Button的外观和大小
        UIButton *btn = self.subviews[i];
        [btn setImage:[UIImage imageNamed:@"xv"] forState:(UIControlStateNormal)];
        [btn setImage:[UIImage imageNamed:@"shi"] forState:(UIControlStateSelected)];
        btn.contentMode = UIViewContentModeCenter;
        btn.userInteractionEnabled = false;
        btn.frame = CGRectMake(originX, originY, bWidth, bHeight);
        
    }
    
    
}
//获取当前的触摸点的坐标
- (CGPoint)pointWithTouches:(NSSet<UITouch *> *)touches
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:touch.view];
    return point;
}
//判断当前点在不在Button中
- (UIButton *)buttonWithPoint:(CGPoint)point
{
    for (int i = 0; i < self.subviews.count; i++) {
        UIButton *btn = self.subviews[i];
        CGFloat wh = btn.frame.size.width - 5;
        //找到Button中心附近25*25的面积
        CGFloat frameX = btn.center.x - wh * 0.5;
        CGFloat frameY = btn.center.y - wh * 0.5;
        //判断这个点在button中心附近25*25的面积内的话就是它了
        if (CGRectContainsPoint(CGRectMake(frameX, frameY, wh, wh), point)) {
            return btn;
        }
    }
    return nil;
}
//设置Button被选中的状态
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //当触摸开始的时候将之前的痕迹抹掉 重新开始
    self.code = [[NSMutableString alloc]init];
    self.isEnd = NO;
    for (int i = 0; i < self.btns.count; i ++) {
        UIButton *btn = self.btns[i];
        btn.selected = NO;
    }
    [self.btns removeAllObjects];
    //找到touch到的点的位置(此处只有一个点 就是开始的那个点)
    CGPoint point = [self pointWithTouches:touches];
    //判断这个点所在的Button
    UIButton *btn = [self buttonWithPoint:point];
    if (btn != nil && btn.selected == false) {
        btn.selected = YES;
        //把button加到数组中
        [self.btns addObject:btn];
        //并记录走过的路线 用Button的tag拼接出的字符串
        [self.code appendString:[NSString stringWithFormat:@"%ld",(long)btn.tag]];
    }
    [self setNeedsDisplay];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //找到touch到的点的位置
    CGPoint point = [self pointWithTouches:touches];
    //判断这个点所在的Button
    UIButton *btn = [self buttonWithPoint:point];
    if (btn != nil && btn.selected == false) {
        btn.selected = YES;
        //把button加到数组中
        [self.btns addObject:btn];
        //并记录走过的路线 用Button的tag拼接出的字符串
        [self.code appendString:[NSString stringWithFormat:@"%ld",(long)btn.tag]];
    }
    [self setNeedsDisplay];
}

//使用touchend清空数据
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //绘制选中的按钮少于3个的时候没有轨迹记录
    if (self.btns.count < 3) {
        for (int i = 0; i < self.btns.count; i ++) {
            UIButton *btn = self.btns[i];
            btn.selected = NO;
        }
        [self.btns removeAllObjects];
    }
    else
    {
        //标志 结束
        self.isEnd = YES;
    }
    [self setNeedsDisplay];
}
//优化 先判空
-(void)drawRect:(CGRect)rect
{
    //如果没有选择的button 则不绘图
    if (self.btns.count == 0) {
        return;
    }
    //使用贝塞尔曲线绘制路线
    UIBezierPath * path = [UIBezierPath bezierPath];
    for (int i = 0; i < self.btns.count; i ++) {
        UIButton *btn = self.btns[i];
        if (i == 0) {
            [path moveToPoint:btn.center];
        }
        else
        {
            [path addLineToPoint:btn.center];
        }
    }
    //设置线的颜色
    [[UIColor colorWithRed:23/255.0 green:171/255.0 blue:227/255.0 alpha:1] set];
    //设置线的宽度
    [path setLineWidth:3];
    //设置连接风格
    [path setLineJoinStyle:(kCGLineJoinRound)];
    //判断是否结束 在touchEnd方法中更改isEnd的值
    if (_isEnd == YES) {
        if (![self.code isEqualToString:self.password]) {
            //修改Path的颜色
            [[UIColor redColor]set];
            //发送通知 提醒ViewController更改Label上的文字
            [[NSNotificationCenter defaultCenter] postNotificationName:@"changeLabel" object:nil];
        }
        else
        {
            //发送通知 提醒ViewController更改Label上的文字
            [[NSNotificationCenter defaultCenter]postNotificationName:@"changeLabel2" object:nil];
        }
    }
    //渲染
    [path stroke];
    
}
@end
使用方法

在ViewController的Xib里设计了界面,其中最下面正方形是关联SpeedDialView的View。


ViewController.m

<span style="font-size:18px;">@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet SpeedDialView *speedDialView;

@end

@implementation ViewController
- (void)viewWillAppear:(BOOL)animated
{
    self.label.text = @"请输入手势密码";
    self.label.textColor = [UIColor darkGrayColor];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(changeLabel) name:@"changeLabel" object:nil];
     [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(changeLabel2) name:@"changeLabel2" object:nil];
   
    self.speedDialView.password = @"1345";
 
    
}
- (void)changeLabel
{
    self.label.text = @"密码输入错误";
    self.label.textColor = [UIColor redColor];
}
- (void)changeLabel2
{
    self.label.text = @"恭喜,密码输入正确";
    self.label.textColor = [UIColor greenColor];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
</span>
最后,就是成果展示了~


大功告成~

我对明天没有期待,因为我对今天满意~

UIButton图片提供


最后,提供Demo地址点击打开链接

我是写的OC版的,感谢简书作者王鑫20111Swift版传送门的无私分享~


实践所写,如有错误,还请指正,谢谢!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值