您的位置:首页 > 移动开发 > IOS开发

iOS开发之手势解锁详解

2016-05-26 11:50 447 查看
A.需求

1.九宫格手势解锁

2.使用了绘图和手势事件

code source: https://github.com/hellovoidworld/GestureUnlockDemo




B.实现

使用按钮来处理每个圆点
使用代码生成按钮
取消按钮点击事件
设置普通状态和选中状态的背景图片
CGRectContainsPoint,移动到按钮范围内改变按钮为选中状态
按钮的连接:使用数组存储被选中的所有按钮,画上连线
已经连线的按钮不需要再连线
触摸结束清空连线和按钮选中状态
移动中也要画出线,最后的点用来辅助画移动中的线
解决bug:每次触摸开始重置当前画笔位置
设置触摸触发选中的按钮内部范围
使用tag记录按钮的选中顺序轨迹,触摸结束取得轨迹
封装整个手势解锁view成为一个自定义控件
封装按钮称为自定类

1.准备基础界面,使用一个UIView作为解锁画面





2.在控制器ViewController设置一下背景图片和状态栏

//
//  ViewController.m
//  HVWLockView
//
//  Created by hellovoidworld on 15/1/12.
//  Copyright (c) 2015年 hellovoidworld. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

// 设置背景
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Home_refresh_bg"]];
}

/** 设置状态栏样式 */
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end


3.自定义解锁画面的类HVWLockView

4.使用代码初始化HVWLockView的子控件—按钮,设置按钮的样式、位置尺寸
//
//  HVWLockView.m
//  HVWLockView
//
//  Created by hellovoidworld on 15/1/12.
//  Copyright (c) 2015年 hellovoidworld. All rights reserved.
//

#import "HVWLockView.h"
#import "HVWLockButton.h"

#define BUTTON_COUNT 9
#define BUTTON_COL_COUNT 3

@implementation HVWLockView

#pragma mark - 初始化方法
/** 使用文件初始化 */
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initView];
}
return self;
}

/** 使用代码初始化 */
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self initView];
}
return self;
}

/** 初始化view内的控件(按钮) */
- (void) initView {
for (int i=0; i<BUTTON_COUNT; i++) {
HVWLockButton *button = [HVWLockButton buttonWithType:UIButtonTypeCustom];

// 取消点击时间
button.userInteractionEnabled = NO;

// 设置指标tag,用来记录轨迹
button.tag = i;

// 设置普通状态图片
[button setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];

// 设置选中状态图片
[button setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];

// 加入按钮到lock view
[self addSubview:button];
}
}

/** 设置按钮位置尺寸 */
- (void)layoutSubviews {
[super layoutSubviews];

// 取出所有按钮
for (int i=0; i<self.subviews.count; i++) {
HVWLockButton *button = self.subviews[i];
CGFloat buttonWidth = 74;
CGFloat buttonHeight = 74;

// 此按钮所在列号
int col = i % BUTTON_COL_COUNT;
// 此按钮所在行号
int row = i / BUTTON_COL_COUNT;
// 等分水平多余空间,计算出间隙
CGFloat marginX = (self.frame.size.width - BUTTON_COL_COUNT * buttonWidth) / (BUTTON_COL_COUNT + 1);
CGFloat marginY = marginX;

// x坐标
CGFloat buttonX = marginX + col * (buttonWidth + marginX);
// y坐标
CGFloat buttonY = marginY + row * (buttonHeight + marginY);

button.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
}
}

@end


out:





5.实现触摸事件方法

(1)点击开始,使被点击的按钮改变为选中状态(改变图片)

(2)点击拖曳中,同样使被触碰到的按钮改变为选中状态

(3)点击结束,清空选中状态

(4)小修改:把HVWLockView背景改为透明

HVWLockView:
UITouch
*touch
=
[touches
anyObject];
CGPoint
touchLocation =
[touch
locationInView:touch.view];
//
检测哪个按钮被点中了
for
(HVWLockButton
*button
in
self.subviews)
{
if
(CGRectContainsPoint(button.frame,
touchLocation))
{
button.selected
=
YES;
}
}
}
-
(void)touchesMoved:(NSSet
*)touches
withEvent:(UIEvent
*)event
{
UITouch
*touch
=
[touches
anyObject];
CGPoint
touchLocation =
[touch
locationInView:touch.view];
//
检测哪个按钮被点中了
for
(HVWLockButton
*button
in
self.subviews)
{
if
(CGRectContainsPoint(button.frame,
touchLocation))
{
button.selected
=
YES;
}
}
}
-
(void)
touchesEnded:(NSSet
*)touches
withEvent:(UIEvent
*)event
{
//
消除所有按钮选中状态
for
(HVWLockButton
*button
in
self.subviews)
{
button.selected
=
NO;
}
}

out:





6.画出连接线

(1)优化:将按钮封装为一个类HVWLockButton
//
//  HVWLockButton.m
//  HVWLockView
//
//  Created by hellovoidworld on 15/1/12.
//  Copyright (c) 2015年 hellovoidworld. All rights reserved.
//

#import "HVWLockButton.h"

@implementation HVWLockButton

/** 使用文件创建会调用 */
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initLockButton];
}
return self;
}

/** 使用代码创建会调用 */
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self initLockButton];
}
return self;
}

/** 初始化 */
- (void) initLockButton {
// 取消交互事件(点击)
self.userInteractionEnabled = NO;

// 设置普通状态图片
[self setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];

// 设置选中状态图片
[self setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
}

@end


(2)使用一个数组来存储已经被选择的按钮

(3)把触碰到的按钮到加入到上述数组中

(4)在绘图方法中把数组内的按钮用线连起来

(5)使用一个成员变量来存储当前触摸位置,画出最后触摸的按钮到现触摸点的线

(6)重复触碰同一个按钮的时候,不用重绘和计算

(7)创建一个代理方法,在触摸结束的时候输出轨迹序列

(8)精简一下代码
#define BUTTON_COUNT 9
#define BUTTON_COL_COUNT 3

@interface HVWLockView()

/** 已选择按钮数组 */
@property(nonatomic, strong) NSMutableArray *selectedButtons;

/** 触摸位置 */
@property(nonatomic, assign) CGPoint currentTouchLocation;

@end

@implementation HVWLockView

/** 初始化数组 */
- (NSMutableArray *)selectedButtons {
if (nil == _selectedButtons) {
_selectedButtons = [NSMutableArray array];
}
return _selectedButtons;
}

#pragma mark - 初始化方法
/** 使用文件初始化 */
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initView];
}
return self;
}

/** 使用代码初始化 */
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self initView];
}
return self;
}

/** 初始化view内的控件(按钮) */
- (void) initView {
// 设置透明背景
self.backgroundColor = [[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:0];

for (int i=0; i<BUTTON_COUNT; i++) {
HVWLockButton *button = [HVWLockButton buttonWithType:UIButtonTypeCustom];

// 设置指标tag,用来记录轨迹
button.tag = i;

// 加入按钮到lock view
[self addSubview:button];
}
}

/** 设置按钮位置尺寸 */
- (void)layoutSubviews {
[super layoutSubviews];

// 取出所有按钮
for (int i=0; i<self.subviews.count; i++) {
HVWLockButton *button = self.subviews[i];
CGFloat buttonWidth = 74;
CGFloat buttonHeight = 74;

// 此按钮所在列号
int col = i % BUTTON_COL_COUNT;
// 此按钮所在行号
int row = i / BUTTON_COL_COUNT;
// 等分水平多余空间,计算出间隙
CGFloat marginX = (self.frame.size.width - BUTTON_COL_COUNT * buttonWidth) / (BUTTON_COL_COUNT + 1);
CGFloat marginY = marginX;

// x坐标
CGFloat buttonX = marginX + col * (buttonWidth + marginX);
// y坐标
CGFloat buttonY = marginY + row * (buttonHeight + marginY);

button.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
}
}

#pragma mark - 触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

[self touchesMoved:touches withEvent:event];

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];

// 检测哪个按钮被点中了
for (HVWLockButton *button in self.subviews) {

// 如果触碰到了此按钮
if (CGRectContainsPoint(button.touchFrame, touchLocation)) {
button.selected = YES;

// 如果此按钮没有被触碰过才进行处理
if (![self.selectedButtons containsObject:button]) {
// 加入到数组
[self.selectedButtons addObject:button];
}
}

// 当前触摸位置
self.currentTouchLocation = touchLocation;
}

// 重绘
[self setNeedsDisplay];
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// 轨迹序列
NSMutableString *passPath = [NSMutableString string];

// 合成轨迹序列
for (HVWLockButton *button in self.selectedButtons) {
// 添加到轨迹序列
[passPath appendFormat:@"%d", button.tag];
}

// 调用代理方法
if ([self.delegate respondsToSelector:@selector(hvwLockView:didFinishedWithPath:)]) {
[self.delegate hvwLockView:self didFinishedWithPath:passPath];
}

// 清除选中状态
[self.selectedButtons makeObjectsPerformSelector:@selector(setSelected:) withObject:@(NO)];

// 清空数组
[self.selectedButtons removeAllObjects];

// 重绘
[self setNeedsDisplay];
}

#pragma mark - 绘图方法
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPath];

// 遍历已选择按钮数组
for (int i=0; i<self.selectedButtons.count; i++) {
HVWLockButton *button = self.selectedButtons[i];

if (0 == i) {
[path moveToPoint:button.center];
} else {
[path addLineToPoint:button.center];
}
}

if (self.selectedButtons.count) {
[path addLineToPoint:self.currentTouchLocation];
}

// 设置画笔
[[UIColor redColor] set];
[path setLineWidth:10];
[path setLineCapStyle:kCGLineCapRound];
[path setLineJoinStyle:kCGLineJoinBevel];

[path stroke];
}

@end


out:





2015-01-12 16:39:23.794 HVWLockView[10274:184387] 手势解锁的输出序列:01246
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: