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=; 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=; i<self.subviews.count; i++) {
HVWLockButton *button = self.subviews[i];
CGFloat buttonWidth = ;
CGFloat buttonHeight = ; // 此按钮所在列号
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 + );
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:
 #pragma mark - 触摸事件
- (void)touchesBegan:(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)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)精简一下代码
 
 //
// 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 @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: green: blue: alpha:]; for (int i=; 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=; i<self.subviews.count; i++) {
HVWLockButton *button = self.subviews[i];
CGFloat buttonWidth = ;
CGFloat buttonHeight = ; // 此按钮所在列号
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 + );
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=; i<self.selectedButtons.count; i++) {
HVWLockButton *button = self.selectedButtons[i]; if ( == i) {
[path moveToPoint:button.center];
} else {
[path addLineToPoint:button.center];
}
} if (self.selectedButtons.count) {
[path addLineToPoint:self.currentTouchLocation];
} // 设置画笔
[[UIColor redColor] set];
[path setLineWidth:];
[path setLineCapStyle:kCGLineCapRound];
[path setLineJoinStyle:kCGLineJoinBevel]; [path stroke];
} @end
 
out:
 
2015-01-12 16:39:23.794 HVWLockView[10274:184387] 手势解锁的输出序列:01246
 
 
 
 
 
 

[iOS UI进阶 - 5.0] 手势解锁Demo的更多相关文章

  1. [iOS UI进阶 - 4.0] 涂鸦app Demo

    A.需求 1.超简易画图,只有一种画笔 2.清屏功能 3.回退功能 4.保存功能 5.使用了cocos2D   code source: https://github.com/hellovoidwor ...

  2. iOS UI进阶-6.0 手势

    给每个页面添加手势,只需要统一设置不是根控制器的页面,都增加手势.需要自定义导航控制器 1.继承代理 @interface BSNavigationController ()<UIGesture ...

  3. [iOS UI进阶 - 3.0] 触摸事件的基本处理

    A.需要掌握和练习的 1.介绍事件类型2.通过按钮的事件处理引出view的事件处理3.响应者对象 --> UIResponder --> UIView4.view的拖拽* 实现触摸方法,打 ...

  4. [iOS UI进阶 - 2.0] 彩票Demo v1.0

    A.需求 1.模仿“网易彩票”做出有5个导航页面和相应功能的Demo 2.v1.0 版本搭建基本框架   code source:https://github.com/hellovoidworld/H ...

  5. iOS UI进阶-1.0 Quartz2D

    概述 Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统.Quartz 2D能完成的工作: 绘制图形 : 线条\三角形\矩形\圆\弧等 绘制文字 绘制\生成图片(图像) 读取\生成PDF ...

  6. [iOS UI进阶 - 6.0] CALayer

    A.基本知识 1.需要掌握的 CALayer的基本属性 CALayer和UIView的关系 position和anchorPoint的作用   2.概念 在iOS中,你能看得见摸得着的东西基本上都是U ...

  7. iOS UI进阶-4.0 地图与定位

    在移动互联网时代,移动app能解决用户的很多生活琐事,比如 导航:去任意陌生的地方 周边:找餐馆.找酒店.找银行.找电影院   在上述应用中,都用到了地图和定位功能,在iOS开发中,要想加入这2大功能 ...

  8. [iOS UI进阶 - 3.2] 手势识别器UIGestureRecognizer

    A.系统提供的手势识别器   1.敲击手势 UITapGestureRecognizer numberOfTapsRequired: 敲击次数 numberOfTouchesRequired: 同时敲 ...

  9. iOS UI进阶-3.0 核心动画

    Core Animation是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍,使用它需要先添加QuartzCore.framework和引入对应的框架<Quar ...

随机推荐

  1. [Codeforces673C]Bear and Colors(枚举,暴力)

    题目链接:http://codeforces.com/contest/673/problem/C 题意:给一串数,不同大小的区间内出现次数最多的那个数在计数的时候会+1,问所有区间都这样计一次数,所有 ...

  2. 分解成3NF保持函数依赖且为无损连接的算法

    分解成3NF保持函数依赖且为无损连接的算法: 1.根据分解成3NF的保持函数依赖的分解算法(http://www.cnblogs.com/bewolf/p/4443919.html),得到分解结果ρ ...

  3. find-all-duplicates-in-an-array(典型的数组中的重复数,不错,我做出来了,可是发现别人有更好的做法)

    https://leetcode.com/problems/find-all-duplicates-in-an-array/ 典型的数组中的重复数.这次是通过跳转法,一个个跳转排查的.因为查过的不会重 ...

  4. Selenium Tutorial (2) - Selenium IDE In Depth

    Installing Firefox and Firebug Installing and Opening Selenium IDE Starting with test cases and test ...

  5. HDU 5301 Buildings 建公寓(逻辑,水)

    题意:有一个包含n*m个格子的矩阵,其中有一个格子已经被染黑,现在要拿一些矩形来填充矩阵,不能填充到黑格子,但是每一个填充进去的矩形都必须至少有一条边紧贴在矩阵的边缘(4条边)的.用于填充的矩形其中最 ...

  6. Swift入门篇-Hello World

    提示:如果您使用手机和平板电脑看到这篇文章,您请在WIFI的环境下阅读,里面有很多图片, 会浪费很多流量. 博主语文一直都不好(如有什么错别字,请您在下评论)望您谅解,没有上过什么学的 最近这2天主要 ...

  7. HTML5之video元素

    一.video元素支持的视频格式 HTML5中的video标签支持3种常用的视频格式: 1.Ogg = 带有Theora 视频编码和Vorbis 音频编码的 Ogg 文件: 2.MPEG4 = 带有H ...

  8. 项目管理工具:Maven使用方法总结

    阅读目录 一.概念 二.Maven安装 三.常用命令 四.生命周期 五.第一个Maven项目 六.POM文件 七.Maven库 八.参考资料 回到顶部 一.概念 Maven是一个项目管理和构建自动化工 ...

  9. mysql explain中key_len的计算

    ken_len表示索引使用的字节数,根据这个值,就可以判断索引使用情况,特别是在组合索引的时候,判断是否所有的索引字段都被查询用到. key_len显示了条件检索子句需要的索引长度,但 ORDER B ...

  10. IOS 五星评分控件

    程序中需要打分的功能,在网上找了几个,都不是很满意.下面是实现出的效果.可以点击,可以拖动. 使用方法:初始化控件. TQStarRatingView *starRatingView = [[TQSt ...