思路:

1.画9个按钮,通过按钮的选中状态控制按钮.

2.连线通过贝塞尔曲线绘制.

3.校验密码通过给按钮绑定tag值判断.

主要代码:

OC版本:

  1 //
  2 //  NineLockView.m
  3 //  lockView
  4 //
  5 //  Created by Shaoting Zhou on 2018/1/24.
  6 //  Copyright © 2018年 Shaoting Zhou. All rights reserved.
  7 //
  8 
  9 #import "NineLockView.h"
 10 
 11 CGFloat const btnCount = 9; //九宫格个数
 12 CGFloat const btnW = 74;    //单个按钮宽
 13 CGFloat const btnH = 74;    //单个按钮高
 14 CGFloat const viewY = 300;  //视图Y
 15 int const columnCount = 3;   //列数
 16 #define kScreenWidth [UIScreen mainScreen].bounds.size.width
 17 
 18 @interface NineLockView ()
 19 @property (nonatomic, strong) NSMutableArray * selectBtnsAry; //选中按钮的数组
 20 @property (nonatomic, assign) CGPoint currentPoint;    //当前的点 坐标 用于判断最后一个点
 21 @end
 22 
 23 
 24 @implementation NineLockView
 25 
 26 -(NSMutableArray *)selectBtnsAry{
 27     if(!_selectBtnsAry){
 28         _selectBtnsAry = [NSMutableArray array];
 29     }
 30     return _selectBtnsAry;
 31 }
 32 
 33 //通过代码布局时会调用这个方法
 34 -(instancetype)initWithFrame:(CGRect)frame{
 35     if(self = [super initWithFrame:frame]){
 36         self.backgroundColor = [UIColor clearColor];
 37         [self addButton];
 38     }
 39     return self;
 40 }
 41 
 42 //通过sb xib布局时会调用这个方法
 43 -(instancetype)initWithCoder:(NSCoder *)aDecoder{
 44     if(self = [super initWithCoder:aDecoder]){
 45         [self addButton];
 46     }
 47     return self;
 48 }
 49 
 50 #pragma Mark - 布局按钮
 51 - (void)addButton{
 52     CGFloat height = 0;;
 53     for (int i = 0; i < btnCount; i++) {
 54         UIButton * btn = [UIButton buttonWithType:(UIButtonTypeCustom)];
 55         btn.tag = i;
 56         btn.userInteractionEnabled = NO; //不可交互
 57         //设置默认的图片
 58         [btn setBackgroundImage:[UIImage imageNamed:@"gesture_normal"] forState:(UIControlStateNormal)];
 59         //设置选中的图片
 60         [btn setBackgroundImage:[UIImage imageNamed:@"gesture_selected"] forState:(UIControlStateSelected)];
 61         int row = i / columnCount;  //第几行
 62         int column = i % columnCount;   //第几列
 63         CGFloat margin = (self.frame.size.width - columnCount * btnW) / (columnCount + 1); //边距
 64         CGFloat btnX = margin + column * (btnW + margin);   //x轴
 65         CGFloat btnY = row * (btnW + margin);       //y轴
 66 //        btn.backgroundColor =[UIColor redColor];
 67         btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
 68         height = btnH + btnY;   //视图高等于 最后一个点的高+y
 69         [self addSubview:btn];
 70     }
 71     self.frame = CGRectMake(0, viewY, kScreenWidth, height);   //视图frame
 72 }
 73 
 74 
 75 - (CGPoint)pointWithTouch:(NSSet *)touches{
 76     UITouch * touch = [touches anyObject];
 77     CGPoint point = [touch locationInView:self];
 78     return point;
 79 }
 80 - (UIButton *)buttonWithPoint:(CGPoint)point{
 81     for (UIButton * btn in self.subviews) {
 82         if(CGRectContainsPoint(btn.frame, point)){
 83             return btn;
 84         }
 85     }
 86     return nil;
 87 }
 88 
 89 
 90 #pragma Mark - 开始移动
 91 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
 92     //1.拿到触摸的点
 93     CGPoint point = [self pointWithTouch:touches];
 94     //2.根据触摸的点拿到相应的按钮
 95     UIButton * btn = [self buttonWithPoint:point];
 96     //3.设置状态
 97     if(btn && btn.selected == NO){
 98         btn.selected = YES;
 99         [self.selectBtnsAry addObject:btn];
100     }
101     
102     
103 }
104 
105 #pragma Mark  - 移动中
106 - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
107     //1.拿到触摸的点
108     CGPoint point = [self pointWithTouch:touches];
109     //2.根据触摸的点拿到相应的按钮
110     UIButton * btn = [self buttonWithPoint:point];
111     //3.设置状态
112     if(btn && btn.selected == NO){
113         btn.selected = YES;
114         [self.selectBtnsAry addObject:btn];
115     }else{
116         self.currentPoint = point;
117     }
118     [self setNeedsDisplay];
119 }
120 
121 #pragma Mark - 结束移动
122 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
123     if([self.delegete respondsToSelector:@selector(lockView:didFinishPath:)]){
124         NSMutableString * path = [NSMutableString string];
125         for (UIButton * btn in self.selectBtnsAry) {
126             [path appendFormat:@"%ld",(long)btn.tag];
127         }
128         [self.delegete lockView:self didFinishPath:path];
129     }
130     //清空状态
131     for(int i = 0; i < self.selectBtnsAry.count; i++ ){
132         UIButton * button = self.selectBtnsAry[i];
133         button.selected = NO;
134     }
135     [self.selectBtnsAry removeAllObjects];
136     [self setNeedsDisplay];
137 }
138 
139 #pragma  Mark - 取消移动
140 - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
141     [self touchesEnded:touches withEvent:event];
142 }
143 
144 #pragma Mark - 绘图
145 - (void)drawRect:(CGRect)rect{
146     if(self.selectBtnsAry.count  == 0){
147         return;
148     }
149     UIBezierPath * path = [UIBezierPath bezierPath];
150     path.lineWidth = 8;
151     path.lineJoinStyle = kCGLineJoinRound;
152     [[UIColor colorWithRed:32/255.0 green:210/255.0 blue:254/255.0 alpha:0.5] set];
153     //遍历按钮
154     for(int i = 0; i < self.selectBtnsAry.count; i++ ){
155         UIButton * button = self.selectBtnsAry[i];
156         NSLog(@"%d",i);
157         if(i == 0){
158             //设置起点
159             [path moveToPoint:button.center];
160         }else{
161             //连线
162             [path addLineToPoint:button.center];
163         }
164 
165     }
166     [path addLineToPoint:self.currentPoint]; //最后一点 连接自己
167     [path stroke];
168 
169 }
170 
171 
172 
173 
174 @end
OC版本
 1 //
 2 //  ViewController.m
 3 //  lockView
 4 //
 5 //  Created by Shaoting Zhou on 2018/1/24.
 6 //  Copyright © 2018年 Shaoting Zhou. All rights reserved.
 7 //
 8 
 9 #import "ViewController.h"
10 #import "NineLockView.h"
11 
12 #define kScreenWidth [UIScreen mainScreen].bounds.size.width
13 #define kScreenHeight [UIScreen mainScreen].bounds.size.height
14 
15 @interface ViewController () <NineLockViewDelegate>
16 
17 @end
18 
19 @implementation ViewController
20 
21 - (void)viewDidLoad {
22     [super viewDidLoad];
23     self.view.backgroundColor = [UIColor brownColor];
24     NineLockView * lockView = [[NineLockView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
25     lockView.delegete = self;
26     [self.view addSubview:lockView];
27 }
28 
29 -(void)lockView:(NineLockView *)lockView didFinishPath:(NSString *)path{
30     if(path.length <= 3){
31         NSLog(@"请至少连4个点");
32         return;
33     }
34     if([path isEqualToString:@"01258"]){
35         NSLog(@"OK");
36     }else{
37         NSLog(@"error");
38     }
39 }
40 
41 - (void)didReceiveMemoryWarning {
42     [super didReceiveMemoryWarning];
43     // Dispose of any resources that can be recreated.
44 }
45 
46 
47 @end
OC版本

swift版本:

 1 //
 2 //  ViewController.swift
 3 //  lockView-Swift
 4 //
 5 //  Created by Shaoting Zhou on 2018/1/25.
 6 //  Copyright © 2018年 Shaoting Zhou. All rights reserved.
 7 //
 8 
 9 import UIKit
10 
11 class ViewController: UIViewController,nineLockViewDelegate  {
12     
13     
14     let kScreenHeight = UIScreen.main.bounds.size.height
15     let kScreenWidth = UIScreen.main.bounds.size.width
16     override func viewDidLoad() {
17         super.viewDidLoad()
18         self.view.backgroundColor = UIColor.brown
19         let nineLockView = NineLockView.init(frame: CGRect.init(x: 0, y: 0, width: kScreenWidth, height: kScreenHeight))
20         nineLockView.delegate = self
21         self.view.addSubview(nineLockView)
22         
23         // Do any additional setup after loading the view, typically from a nib.
24     }
25     //MARK: - 代理方法
26     func lockView(lockView: NineLockView, path: String) {
27         if(path.description.count < 4){
28             print("至少连4个");
29             return;
30         }
31         if(path == "01258"){
32             print("密码正确");
33         }else{
34             print("密码错误");
35         }
36     }
37     
38     
39     override func didReceiveMemoryWarning() {
40         super.didReceiveMemoryWarning()
41         // Dispose of any resources that can be recreated.
42     }
43     
44     
45 }
Swift版本
  1 //
  2 //  NineLockView.swift
  3 //  lockView-Swift
  4 //
  5 //  Created by Shaoting Zhou on 2018/1/25.
  6 //  Copyright © 2018年 Shaoting Zhou. All rights reserved.
  7 //
  8 
  9 import UIKit
 10 
 11 let btnCount = 9; //九宫格个数
 12 let btnW:CGFloat = 74.0;    //单个按钮宽
 13 let btnH:CGFloat = 74.0;    //单个按钮高
 14 let viewY:CGFloat = 300.0;  //视图Y
 15 let columnCount = 3;   //列数
 16 let kScreenHeight = UIScreen.main.bounds.size.height
 17 let kScreenWidth = UIScreen.main.bounds.size.width
 18 
 19 //创建协议
 20 protocol nineLockViewDelegate:NSObjectProtocol
 21 {
 22     func lockView(lockView:NineLockView,path:String)
 23 }
 24 
 25 class NineLockView: UIView {
 26     weak var delegate:nineLockViewDelegate?
 27     var selectBtnsAry = [UIButton] ()
 28     var currentPoint:CGPoint!
 29     
 30     override init(frame: CGRect) {
 31         super.init(frame: frame)
 32         self.backgroundColor = UIColor.clear
 33         self .addButton();
 34     }
 35     
 36     // MARK: 添加按钮
 37     func addButton(){
 38         var height:CGFloat = 0.0
 39         for var i in 0 ..< btnCount{
 40             let btn = UIButton.init(type: .custom)
 41             btn.setImage(#imageLiteral(resourceName: "gesture_normal"), for: .normal)  //默认图片
 42             btn.setImage(#imageLiteral(resourceName: "gesture_selected"), for: .selected)  //选中图片
 43             btn.tag = i
 44             btn.isUserInteractionEnabled = false  //取消用户交互
 45             let row = i / columnCount  //行数
 46             let column = i % columnCount //列数
 47             let margin:CGFloat = (self.frame.size.width - CGFloat(columnCount) * btnW) / CGFloat( (columnCount + 1)); //边距
 48             let btnX:CGFloat = margin + CGFloat(column) * (btnW + margin);   //x轴
 49             let btnY:CGFloat = CGFloat(row) * (btnW + margin);       //y轴
 50             btn.frame = CGRect.init(x: btnX, y: btnY, width: btnW, height: btnH)
 51             height = btnY + btnH   //视图的高 = 最后一个按钮的高 + y
 52             self.addSubview(btn)
 53         }
 54         self.frame = CGRect.init(x: 0, y: viewY, width: kScreenWidth, height: height)
 55     }
 56     
 57     func pointWithTouch(touches:Set<UITouch>) -> CGPoint{
 58         let touch:UITouch = (touches as NSSet).anyObject() as! UITouch
 59         let point = touch.location(in: self)
 60         return point;
 61     }
 62     func buttonWithPoint(point:CGPoint) -> UIButton?{
 63         for var view:UIView in self.subviews {
 64             let btn:UIButton =  view as! UIButton
 65             if(btn.frame.contains(point)){
 66                 return btn
 67             }
 68         }
 69         return nil
 70     }
 71     
 72     
 73     // MARK:  开始移动
 74     override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
 75         //1.拿到触摸的点
 76         let point:CGPoint = self.pointWithTouch(touches: touches)
 77         //2.根据触摸的点拿到相应的按钮
 78         guard let btn:UIButton = self.buttonWithPoint(point: point) else{
 79             return;
 80         }
 81         //3.设置状态
 82         if(btn.isSelected == false){
 83             btn.isSelected  = true
 84             self.selectBtnsAry.append(btn)
 85         }
 86         
 87     }
 88     
 89     // MARK:  移动中
 90     override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
 91         //1.拿到触摸的点
 92         let point:CGPoint = self.pointWithTouch(touches: touches)
 93         //2.根据触摸的点拿到相应的按钮
 94         guard let btn:UIButton = self.buttonWithPoint(point: point) else{
 95             return;
 96         }
 97         //3.设置状态
 98         if(btn.isSelected == false){
 99             btn.isSelected  = true
100             self.selectBtnsAry.append(btn)
101         }else{
102             self.currentPoint = point;
103         }
104         self.setNeedsDisplay()
105 
106     }
107     // MARK:  移动停止
108     override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
109         if delegate != nil{
110             var str = "";
111             for var i in 0 ..< self.selectBtnsAry.count{
112                 let btn:UIButton = self.selectBtnsAry[i]
113                 str = str  + String(btn.tag)
114             }
115             self.delegate?.lockView(lockView: self, path: str)
116         }
117         
118         for var i in 0 ..< self.selectBtnsAry.count{
119             let btn:UIButton = self.selectBtnsAry[i]
120             btn.isSelected = false
121         }
122         self.selectBtnsAry.removeAll()
123         self.setNeedsDisplay()
124     }
125     
126     // MARK:  移动取消
127     override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
128         self.touchesEnded(touches, with: event)
129     }
130     
131     // MARK:  绘图
132     override func draw(_ rect: CGRect) {
133         if(self.selectBtnsAry.count == 0){
134             return;
135         }
136         let  path:UIBezierPath = UIBezierPath.init()
137         path.lineWidth = 8
138         path.lineJoinStyle = .round
139         UIColor.init(red: 32/255.0, green: 210/255.0, blue: 254/255.0, alpha: 0.5).set()
140         //遍历按钮
141         for var i in 0 ..< self.selectBtnsAry.count{
142             let btn:UIButton = self.selectBtnsAry[i]
143             if(i == 0){
144                 //起点
145                 path.move(to: btn.center)
146             }else{
147                 //划线
148                 path.addLine(to: btn.center)
149             }
150         }
151         path.addLine(to: self.currentPoint) //最后一点 连接自己
152         path.stroke()
153     }
154     
155     
156     required init?(coder aDecoder: NSCoder) {
157         fatalError("init(coder:) has not been implemented")
158     }
159     
160 }
Swift版本

 

 

基本演示:

这里的只演示下基本九宫格校验密码(密码已经存在:01258).

创建密码,修改密码思路类似.

github: https://github.com/pheromone/iOS-nineLock