zoukankan      html  css  js  c++  java
  • iOS_39_触摸解锁

    终于效果图:

    控制器:
    //
    //  BeyondViewController.m
    //  39_触摸解锁
    //
    //  Created by beyond on 14-9-17.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //
    
    #import "BeyondViewController.h"
    #import "LockView.h"
    #import "LockViewDelegate.h"
    
    // 遵守协议,用于获知,用户在LockView上画的解锁路径
    @interface BeyondViewController ()<LockViewDelegate>
    
    @end
    
    @implementation BeyondViewController
    
    
    #pragma mark - LockView代理方法
    // 用于获知,用户在LockView上画的解锁路径
    - (void)lockView:(LockView *)lockView didFinishPath:(NSString *)path
    {
        [MBProgressHUD showSuccess:path];
        NSLog(@"获得用户的手势路径:%@", path);
    }
    
    @end<span style="font-family:Courier New;color:#393939;"><span style="font-size: 24px; line-height: 26px; background-color: rgb(245, 245, 245);"><strong>
    </strong></span></span>


    自己定义CircleButton
    //
    //  Circle.m
    //  39_触摸解锁
    //
    //  Created by beyond on 14-9-17.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //
    
    #import "Circle.h"
    
    @implementation Circle
    
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            // 自己定义方法,初始化
            [self setup];
        }
        return self;
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder
    {
        if (self = [super initWithCoder:aDecoder]) {
            // 自己定义方法,初始化
            [self setup];
        }
        return self;
    }
    
    // 自己定义方法,初始化
    - (void)setup
    {
        // 设置button不可用(不可点击)
        self.userInteractionEnabled = NO;
        
        // 设置默认的背景图片
        [self setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
        
        // 设置选中时的背景图片(selected)
        [self setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
    }
    
    @end
    


    LockView定义的协议
    //
    //  LockViewDelegate.h
    //  39_触摸解锁
    //
    //  Created by beyond on 14-9-17.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  自己定义的LockView的代理必需 遵守的协议
    
    #import <Foundation/Foundation.h>
    
    @class LockView;
    
    @protocol LockViewDelegate <NSObject>
    
    
    @optional
    // 锁屏手势绘制完毕时,调用本方法,通知外界
    - (void)lockView:(LockView *)lockView didFinishPath:(NSString *)path;
    
    @end
    


    核心代码,自己定义的LockView
    //
    //  LockView.h
    //  39_触摸解锁
    //
    //  Created by beyond on 14-9-17.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    @protocol LockViewDelegate;
    @interface LockView : UIView
    
    // 代理,为当前控制器,目的是,解锁手势绘制完毕后,通知它
    @property (nonatomic, weak) IBOutlet id<LockViewDelegate> delegate;
    
    @end
    


    //
    //  LockView.m
    //  39_触摸解锁
    //
    //  Created by beyond on 14-9-17.
    //  Copyright (c) 2014年 com.beyond. All rights reserved.
    //  重点,核心~~~
    
    #import "LockView.h"
    #import "Circle.h"
    #import "LockViewDelegate.h"
    
    
    @interface LockView()
    // 数组,记住全部的touchMove经过的circle
    @property (nonatomic, strong) NSMutableArray *circleArr;
    // 手指一直与屏幕保持触摸的点,活动的点,用于绘制最后一个circle的中心到 用户触摸点的线段
    @property (nonatomic, assign) CGPoint activePoint;
    @end
    
    
    @implementation LockView
    
    
    #pragma mark - 懒载入
    // 数组,记住全部的touchMove经过的circle
    - (NSMutableArray *)circleArr
    {
        if (_circleArr == nil) {
            _circleArr = [NSMutableArray array];
        }
        return _circleArr;
    }
    #pragma mark - 初始化
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            // 调用自己定义方法,一次性加入全部的button
            [self addAllCircles];
        }
        return self;
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder
    {
        if (self = [super initWithCoder:aDecoder]){
            // 调用自己定义方法,一次性加入全部的button
            [self addAllCircles];
        }
        return self;
    }
    // 调用自己定义方法,一次性加入全部的button
    - (void)addAllCircles
    {
        for (int index = 0; index<9; index++) {
            // 创建自己定义button,绑定tag目的是,将来用于验证锁的顺序
            Circle *btn = [Circle buttonWithType:UIButtonTypeCustom];
            btn.tag = index;
            // 加入button
            [self addSubview:btn];
        }
    }
    #pragma mark - 父类方法
    // 每加入一个circle,就调整一下全部子控件的frame
    - (void)layoutSubviews
    {
        // 必须先调用父类的方法
        [super layoutSubviews];
        // 遍历全部的子button,又一次调整frame
        for (int i = 0; i<self.subviews.count; i++) {
            // 取出button
            Circle *btn = self.subviews[i];
            
            // 设置frame
            CGFloat btnW = 74;
            CGFloat btnH = 74;
            // 共三列
            int totalColumns = 3;
            // 第i个button 所在的列
            int col = i % totalColumns;
            // 第i个button 所在的行
            int row = i / totalColumns;
            // 间距均分
            CGFloat marginX = (self.frame.size.width - totalColumns * btnW) / (totalColumns + 1);
            CGFloat marginY = marginX;
            // 设置frame
            CGFloat btnX = marginX + col * (btnW + marginX);
            CGFloat btnY = row * (btnH + marginY);
            btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
        }
    }
    
    #pragma mark - 触摸方法
    // 手指按下
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        // 1.必须先清空上一次(手指与屏幕一直接触的点)
        self.activePoint = CGPointZero;
        
        // 2.调用自己定义方法,获得触摸開始的这个点
        CGPoint startPoint = [self pointWithTouches:touches];
        
        // 3.调用自己定义方法,获得触摸到的button(假设有的话)
        Circle *btn = [self buttonWithPoint:startPoint];
        
        // 4.设置该被触摸到的button的状态(假设有)
        if (btn && btn.selected == NO) {
            btn.selected = YES;
            // button数组中没有,才要加入到对象数组
            [self.circleArr addObject:btn];
        }
        
        // 5.刷新界面,以下方法会自己主动调用drawRect
        [self setNeedsDisplay];
    }
    // 手指移动(调用频率相当高)
    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    {
        // 1.调用自己定义方法,获得触摸过程中的点
        CGPoint pos = [self pointWithTouches:touches];
        
        // 2.调用自己定义方法,获得触摸移动时碰到的button(假设有的话)
        Circle *btn = [self buttonWithPoint:pos];
        
        // 3.设置该被触摸移动时碰到的button的状态(假设有)
        if (btn && btn.selected == NO) {
            // 摸到了button
            btn.selected = YES;
            // button数组中没有,才要加入到对象数组
            [self.circleArr addObject:btn];
        } else {
            // 没有摸到button,就更新activePoint的位置,以便drawRect
            self.activePoint = pos;
        }
        // 4.刷新界面,以下方法会自己主动调用drawRect
        [self setNeedsDisplay];
    }
    // 触摸结束(手抬起了),通知代理,
    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
        // 1.通知代理
        if ([self.delegate respondsToSelector:@selector(lockView:didFinishPath:)]) {
            NSMutableString *path = [NSMutableString string];
            for (Circle *btn in self.circleArr) {
                // tag此时,起到关键作用
                [path appendFormat:@"%d", btn.tag];
            }
            [self.delegate lockView:self didFinishPath:path];
        }
        // 2.可有可无,清空全部的button选中状态(恢复原样)
        [self.circleArr makeObjectsPerformSelector:@selector(setSelected:) withObject:@(NO)];
        
        // 3.清空选中的button对象数组(为下一次手指按下做准备)
        [self.circleArr removeAllObjects];
        // 4.刷新界面,以下方法会自己主动调用drawRect
        [self setNeedsDisplay];
    }
    // 触摸被打断(相似结束)
    - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
    {
        [self touchesEnded:touches withEvent:event];
    }
    
    #pragma mark - 抽取的自己定义方法
    // 自己定义方法,依据touches集合获得相应的触摸点位置坐标
    - (CGPoint)pointWithTouches:(NSSet *)touches
    {
        UITouch *touch = [touches anyObject];
        // 返回手指触摸点在相应view中的坐标
        return [touch locationInView:touch.view];
    }
    
    // 自己定义方法,依据触摸点位置坐标,遍历全部button,获得被碰手指到的button(假设有)
    - (Circle *)buttonWithPoint:(CGPoint)point
    {
        for (Circle *btn in self.subviews) {
            //        CGFloat wh = 24;
            //        CGFloat frameX = btn.center.x - wh * 0.5;
            //        CGFloat frameY = btn.center.y - wh * 0.5;
            //        if (CGRectContainsPoint(CGRectMake(frameX, frameY, wh, wh), point)) {
            if (CGRectContainsPoint(btn.frame, point)) {
                return btn;
            }
        }
        
        return nil;
    }
    
    
    #pragma mark - 核心~画图
    // 最后一步
    - (void)drawRect:(CGRect)rect
    {
        // 假设没有碰到不论什么button,直接返回,不用绘线
        if (self.circleArr.count == 0) return;
        
        // 1.開始一个贝塞尔路径
        UIBezierPath *path = [UIBezierPath bezierPath];
        
        // 2.遍历全部的button,将碰到的button的中心加入到路径中
        for (int i = 0; i<self.circleArr.count; i++) {
            Circle *btn = self.circleArr[i];
            if (i == 0) {
                // 设置路径起点
                [path moveToPoint:btn.center];
            } else {
                // 加入到路径
                [path addLineToPoint:btn.center];
            }
        }
        
        // 3.连接最后一条,即最后一个碰到的button中心 至 activePoint
        if (CGPointEqualToPoint(self.activePoint, CGPointZero) == NO) {
            // 必须先进行检測
            [path addLineToPoint:self.activePoint];
        }
        
        // 4.设置画图属性
        path.lineWidth = 8;
        // 圆接头
        path.lineJoinStyle = kCGLineJoinBevel;
        // 线条天蓝色
        [[UIColor colorWithRed:32/255.0 green:210/255.0 blue:254/255.0 alpha:0.5] set];
        // 5.闭合贝塞尔路径
        [path stroke];
    }
    
    @end
    


    storyboard


  • 相关阅读:
    C++ 11 右值引用以及std::move
    poj2299--B
    Linux Socket编程注意事项
    Using Qt to build an Omi App for iOS (and Android)
    openwrt 3g模块上网
    详谈隐藏Tabbar的几种方法
    ZOJ 3529 A Game Between Alice and Bob(博弈论-sg函数)
    uva 10574
    【MySQL案例】HA: GTID_MODE配置不一致
    Swift UIView 层次调整
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/6783185.html
Copyright © 2011-2022 走看看