首先看下我們要制作功能的效果如圖所示:
![效果圖](https://raw.githubusercontent.com/iosyueshen/DYHLockViewDemo/master/DYHLockViewDemo/%E6%89%8B%E5%8A%BF%E5%AF%86%E7%A0%81.gif)
效果圖
思路介紹
- 手勢密碼一般為9宮格模式晚伙,通過手勢滑動設(shè)置一個
多邊形
(polygon
)的密碼圖片矗蕊。 - 9宮格的每一個
格子
可以用按鈕
表示節(jié)點
,也可以用CAShapeLayer
來繪制節(jié)點
壁顶。 - 在這里采用
CAShapeLayer
來繪制9宮格的節(jié)點
副砍,手勢密碼的節(jié)點有選中模式
、正常模式
和警告模式
三種狀態(tài)漂佩,特別說明:
警告模式是在第二次密碼輸入與第一次密碼不一致脖含,或者解鎖密碼錯誤
的時候提示的狀態(tài)。正常模式
只是一個空心圓
投蝉,選中模式
是一個內(nèi)部的實心圓
+外部的空心圓
养葵,警告模式
只是改變選中模式的顏色,所以這里采用兩個CAShapeLayer
對象來繪制外部空心圓
和內(nèi)部實心圓
(outlineLayer
和innerCircleLayer
)瘩缆。 - 創(chuàng)建節(jié)點類
LockNodeView
和密碼視圖類LockView
关拒,LockView采用maskView
方式添加密碼視圖,9宮格用[maskPath appendPath:[UIBezierPath bezierPathWithOvalInRect:frame]
來繪制庸娱,frame為每個節(jié)點
的坐標(biāo)着绊,9個節(jié)點通過UIBezierPath
統(tǒng)一繪制成一個圖形,這樣有利于管理熟尉。 - 重寫視圖的
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
和-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
方法归露,也可以用滑動手勢
代替。 - 滑動的時候去判斷手勢坐標(biāo)滑到了哪一個
節(jié)點
斤儿,并且判斷節(jié)點
是否已經(jīng)被選中剧包,未選中添加連線恐锦。 - 在滑動結(jié)束的時候獲取保存
節(jié)點
的數(shù)組
,連接節(jié)點
的tag
組成一個字符串
疆液,作為手勢結(jié)果密碼
一铅。
節(jié)點類
@interface DYHLockNodeView ()
/**
手勢外部的空心圓
*/
@property (nonatomic, strong) CAShapeLayer *outlineLayer;
/**
手勢內(nèi)部的實心圓
*/
@property (nonatomic, strong) CAShapeLayer *innerCircleLayer;
@end
- (void)layoutSubviews {
self.outlineLayer.frame = self.bounds;
UIBezierPath *outlinePath = [UIBezierPath bezierPathWithOvalInRect:self.bounds];
self.outlineLayer.path = outlinePath.CGPath;
//中心圓的直徑為外部圓的1/3
CGFloat innerWidth = self.bounds.size.width / 3;
self.innerCircleLayer.frame = CGRectMake(innerWidth, innerWidth, innerWidth, innerWidth);
UIBezierPath *innerPath = [UIBezierPath bezierPathWithOvalInRect:self.innerCircleLayer.bounds];
self.innerCircleLayer.path = innerPath.CGPath;
}
- (void)setStatusNormal {
self.outlineLayer.strokeColor = [UIColor whiteColor].CGColor;
self.innerCircleLayer.fillColor = [UIColor clearColor].CGColor;
}
- (void)setStatusSelected {
self.outlineLayer.strokeColor = LIGHTBLUE.CGColor;
self.innerCircleLayer.fillColor = LIGHTBLUE.CGColor;
}
- (void)setStatusWarning {
self.outlineLayer.strokeColor = [UIColor redColor].CGColor;
self.innerCircleLayer.fillColor = [UIColor redColor].CGColor;
}
密碼視圖類
初始化添加9宮格節(jié)點,然后在layoutSubviews中布局
[self.layer addSublayer:self.polygonLineLayer];
_nodes = [NSMutableArray arrayWithCapacity:9];
for (int i = 0; i < 9; i++) {
DYHLockNodeView *nodeView = [[DYHLockNodeView alloc] init];
nodeView.tag = i;
[_nodes addObject:nodeView];
[self addSubview:nodeView];
}
_selectedNodes = [NSMutableArray arrayWithCapacity:9];
_points = [NSMutableArray array];
繪制九宮格
- (void)layoutSubviews {
//添加一個mask層
self.polygonLineLayer.frame = self.bounds;
CAShapeLayer *maskLayer = [CAShapeLayer new];
maskLayer.frame = self.bounds;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:self.bounds];
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.lineWidth = 1.0f;
maskLayer.strokeColor = [UIColor blackColor].CGColor;
maskLayer.fillColor = [UIColor blackColor].CGColor;
//每行3個nodeView堕油,沒列3個潘飘,上下左右間距為一個nodeView的寬
for (int i = 0; i < self.nodes.count; i++) {
DYHLockNodeView *nodeView = self.nodes[i];
CGFloat min = self.bounds.size.width < self.bounds.size.height ? self.bounds.size.width : self.bounds.size.height;
CGFloat width = min / 5;
CGFloat height = width;
int row = i % 3;
int column = i / 3;
CGRect frame = CGRectMake(row * (width * 2), column * (width * 2), width, height);
nodeView.frame = frame;
[maskPath appendPath:[UIBezierPath bezierPathWithOvalInRect:frame]];
}
maskLayer.path = maskPath.CGPath;
self.polygonLineLayer.mask = maskLayer;
}
手勢滑動判斷滑動位置
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = touches.anyObject;
CGPoint touchPoint = [touch locationInView:self];
// 查找手指滑過的node
NSInteger index = [self indexForNodeAtPoint:touchPoint];
if (index >= 0) {
DYHLockNodeView *node = self.nodes[index];
if (![self addSelectedNode:node]) {
//移動線到手勢位置
[self moveLineWithFingerPosition:touchPoint];
}
} else {
[self moveLineWithFingerPosition:touchPoint];
}
}
手勢滑動結(jié)束
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//手勢結(jié)束的時候,清除超出節(jié)點的線掉缺,起點在第一個節(jié)點中心福也,結(jié)束也是最后一個節(jié)點中心,超出部分不需要繪制
[self removeLastFingerPosition];
if([self.delegate respondsToSelector:@selector(lockView:didEndSwipeWithPassword:)]) {
NSMutableString *password = [NSMutableString new];
for(DYHLockNodeView *nodeView in self.selectedNodes) {
NSString *index = [@(nodeView.tag) stringValue];
[password appendString:index];
}
self.viewState = [self.delegate lockView:self didEndSwipeWithPassword:password];
} else {
self.viewState = DYHLockViewStateSelected;
}
}