UIGestureRecognizer - BNR
继续上篇UITouch - BNR。该篇将实现线条选择、移动和删除操作。
UIGestureRecognizer有一系列子类,每一个子类都用于识别特定的手势。当识别出一个手势时,手势识别器会拦截视图的触摸事件。
使用UITapGestureRecognizer类,实现当用户连续点击屏幕两次时,全部线条都被清空。
修改BNRDrawView类的initWithFrame:方法如下:
- (instancetype)initWithFrame:(CGRect)r {
self = [super initWithFrame:r];
if (self) {
self.linesInProgress = [[NSMutableDictionary alloc] init];
self.finishedLines = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor grayColor];
self.multipleTouchEnabled = YES;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(doubleTap:)];
doubleTapRecognizer.numberOfTapsRequired = ;
doubleTapRecognizer.delaysTouchesBegan = YES; //使第一次点击不产生红点 [self addGestureRecognizer:doubleTapRecognizer];
}
return self;
}
实现连续两次点击的doubleTap:响应事件,如下:
- (void)doubleTap:(UIGestureRecognizer *)gr {
NSLog(@"Recognized Double Tap");
[self.linesInProgress removeAllObjects];
[self.finishedLines removeAllObjects];
[self setNeedsDisplay];
}
添加手势识别,允许用户删除一条选定的线条。修改BNRDrawView类的initWithFrame:方法如下:
- (instancetype)initWithFrame:(CGRect)r {
self = [super initWithFrame:r];
if (self) {
self.linesInProgress = [[NSMutableDictionary alloc] init];
self.finishedLines = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor grayColor];
self.multipleTouchEnabled = YES;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(doubleTap:)];
doubleTapRecognizer.numberOfTapsRequired = ;
doubleTapRecognizer.delaysTouchesBegan = YES;
[self addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(tap:)];
tapRecognizer.delaysTouchesBegan = YES;
[tapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; //区别点击与双击手势
[self addGestureRecognizer:tapRecognizer];
}
return self;
}
实现单击的tap:响应事件,如下:
- (void)tap:(UIGestureRecognizer *)gr {
NSLog(@"Recognized Tap");
}
在BNRDrawView.m文件的类扩展中声明selectedLine属性,用来表示被用户选中的线条,如下:
@property (nonatomic, weak) BNRLine *selectedLine;
其中,finishedLines数组拥有对selectedLine的强引用。当selectedLine从finishedLines中被删除时,将变为nil。
修改BNRDrawView类中drawRect:方法,使selectedLine显示绿色,如下:
- (void)drawRect:(CGRect)rect {
[[UIColor blackColor] set];
for (BNRLine *line in self.finishedLines) {
[self strokeLike:line];
}
[[UIColor redColor] set];
for (NSValue *key in self.linesInProgress) {
[self strokeLike:self.linesInProgress[key]];
}
if(self.selectedLine) {
[[UIColor greenColor] set];
[self strokeLike:self.selectedLine];
}
}
获取接近点击处的线条:
- (BNRLine *)lineAtPoint:(CGPoint)p {
for(BNRLine *l in self.finishedLines) {
CGPoint start = l.begin;
CGPoint end = l.end;
for (float t = 0.0; t <= 1.0; t += 0.05) {
float x = start.x + t * (end.x - start.x);
float y = start.y + t * (end.y - start.y);
if (hypot(x - p.x, y - p.y) < 20.0) {
return l;
}
}
}
return nil;
}
最后修改点击手势响应事件tap:方法如下:
- (void)tap:(UIGestureRecognizer *)gr {
NSLog(@"Recognized Tap");
CGPoint point = [gr locationInView:self];
self.selectedLine = [self lineAtPoint:point];
[self setNeedsDisplay];
}
接下来实现,当用户已经选中一条线条之后,将显示一个菜单提供对该线条的删除操作。
修改tap:方法如下:
- (void)tap:(UIGestureRecognizer *)gr {
NSLog(@"Recognized Tap");
CGPoint point = [gr locationInView:self];
self.selectedLine = [self lineAtPoint:point];
if(self.selectedLine) {
//使该视图成为menu item响应方法的目标
[self becomeFirstResponder];
UIMenuController *menu = [UIMenuController sharedMenuController];
//创建一个新的删除UIMenuItem
UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:@"Delete" action:@selector(deleteLine:)];
menu.menuItems = @[deleteItem];
//设置menu的显示位置
[menu setTargetRect:CGRectMake(point.x, point.y, , ) inView:self];
[menu setMenuVisible:YES animated:YES];
} else {
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
}
[self setNeedsDisplay];
}
当自定义的视图类需要成为第一响应者时,必须重载canBecomeFirstResponder方法,在BNRDrawView.m中添加如下方法:
- (BOOL)canBecomeFirstResponder {
return YES;
}
如果menu items的响应方法没有实现,该menu就不会显示。实现deleteLine:方法如下:
- (void)deleteLine:(id)sender {
[self.finishedLines removeObject:self.selectedLine];
[self setNeedsDisplay];
}
运行程序,效果如下:

接下来,实现如下功能,当用户长按住一条线时,该线将被选中,用户能够用手指拖动该线条。修改initWithFrame:方法如下:
- (instancetype)initWithFrame:(CGRect)r {
self = [super initWithFrame:r];
if (self) {
self.linesInProgress = [[NSMutableDictionary alloc] init];
self.finishedLines = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor grayColor];
self.multipleTouchEnabled = YES;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(doubleTap:)];
doubleTapRecognizer.numberOfTapsRequired = ;
doubleTapRecognizer.delaysTouchesBegan = YES;
[self addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(tap:)];
tapRecognizer.delaysTouchesBegan = YES;
[tapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; //区别点击与双击手势
[self addGestureRecognizer:tapRecognizer];
UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:@selector(longPress:)];
[self addGestureRecognizer:pressRecognizer];
}
return self;
}
手势识别器处理长按时,其state属性会经历三种变化,其为 UIGestureRecognizerStatePossible UIGestureRecognizerStateBegan UIGestureRecognizerStateEnded 。
实现长按的响应方法longPress:如下:
- (void)longPress:(UIGestureRecognizer *)gr {
if (gr.state == UIGestureRecognizerStateBegan) {
CGPoint point = [gr locationInView:self];
self.selectedLine = [self lineAtPoint:point];
if (self.selectedLine) {
[self.linesInProgress removeAllObjects];
}
} else if (gr.state == UIGestureRecognizerStateEnded) {
self.selectedLine = nil;
}
[self setNeedsDisplay];
}
让BNRDrawView遵守UIGestureRecognizerDelegate协议,并添加一个UIPanGestureRecognizer属性,如下:
@interface BNRDrawView () <UIGestureRecognizerDelegate> @property (nonatomic, strong) UIPanGestureRecognizer *moveRecognizer;
@property (nonatomic, strong) NSMutableDictionary *linesInProgress;
@property (nonatomic, strong) NSMutableArray *finishedLines;
@property (nonatomic, weak) BNRLine *selectedLine; @end
修改initWithFrame:方法。此处cancelsTouchesInView属性默认为YES,意味着手势识别器将吞没任何它识别出的手势,这样的话,视图将没有机会对UIRespnder方法做出响应。将其设置为NO,这样,手势识别器能识别的触摸事件也会被UIResponder识别。代码如下:
- (instancetype)initWithFrame:(CGRect)r {
self = [super initWithFrame:r];
if (self) {
self.linesInProgress = [[NSMutableDictionary alloc] init];
self.finishedLines = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor grayColor];
self.multipleTouchEnabled = YES;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(doubleTap:)];
doubleTapRecognizer.numberOfTapsRequired = ;
doubleTapRecognizer.delaysTouchesBegan = YES;
[self addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(tap:)];
tapRecognizer.delaysTouchesBegan = YES;
[tapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; //区别点击与双击手势
[self addGestureRecognizer:tapRecognizer];
UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:@selector(longPress:)];
[self addGestureRecognizer:pressRecognizer];
self.moveRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveLine:)];
self.moveRecognizer.delegate = self;
self.moveRecognizer.cancelsTouchesInView = NO;
[self addGestureRecognizer:self.moveRecognizer];
}
return self;
}
实现UIGestureRecognizerDelegate协议的gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:方法,如果该方法返回YES,手势识别器将与其它的识别器分享该触摸事件。如下:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if (gestureRecognizer == self.moveRecognizer) {
return YES;
}
return NO;
}
实现移动手势的响应事件moveLine:,如下:
- (void)moveLine:(UIPanGestureRecognizer *)gr {
if (!self.selectedLine) {
return;
}
if (gr.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [gr translationInView:self];
CGPoint begin = self.selectedLine.begin;
CGPoint end = self.selectedLine.end;
begin.x += translation.x;
begin.y += translation.y;
end.x += translation.x;
end.y += translation.y;
self.selectedLine.begin = begin;
self.selectedLine.end = end;
[self setNeedsDisplay];
[gr setTranslation:CGPointZero inView:self];
}
}
程序代码链接:http://pan.baidu.com/s/1o6Dz5xg
UIGestureRecognizer - BNR的更多相关文章
- iOS UIGestureRecognizer与UIMenuController(内容根据iOS编程)
UIGestureRecognizer 对象会截取本应由视图处理的触摸事件.当某个UIGestureRecognizer对象识别出特定的手势后,就会向指定的对象发送指定的消息.iOS SDK默认提供若 ...
- iOS 手势识别器(UIGestureRecognizer)
UIGestureRecognizer是一个抽象类,定义了所有手势的基本行为,使用它的子类才能处理具体的手势. UIGestureRecognizer的子类有: UITapGestureRecogni ...
- 手势(UIGestureRecognizer)
通过继承 UIGestureRecognizer 类,实现自定义手势(手势识别器类). PS:自定义手势时,需要 #import <UIKit/UIGestureRecognizerSubcla ...
- UIKit框架之UIGestureRecognizer
---恢复内容开始--- 1.继承链:NSObject 2.UIGestureRecognizer的子类有以下: UITapGestureRecognizer :点击 UIPinchGestureRe ...
- 你真的了解UIGestureRecognizer吗?
一:首先查看一下关于UIGestureRecognizer的定义 //当前手势状态 typedef NS_ENUM(NSInteger, UIGestureRecognizerState) { //尚 ...
- UIGestureRecognizer
•为了完成手势识别,必须借助于手势识别器----UIGestureRecognizer • •利用UIGestureRecognizer,能轻松识别用户在某个view上面做的一些常见手势 • •UIG ...
- IOS手势UIGestureRecognizer
UIGestureRecognizer是一个抽象类,定义了所有手势的基本行为,它有6个子类处理具体的手势: 1.UITapGestureRecognizer (任意手指任意次数的点击) // 点击次数 ...
- iOS基础篇(十七)——UIGestureRecognizer用法
UIGestureRecognizer(手势识别)在iOS 中非常重要,他极大地提高了移动设备的使用便捷性: 在3.2之前是主要使用的是由UIResponder而来的如下4种方式: - (void)t ...
- 点击事件touches与ios的手势UIGestureRecognizer
.h文件 @property (weak,nonatomic) IBOutlet UILabel *messageLabel;@property (weak,nonatomic) IBOutlet U ...
随机推荐
- Java架构师趣谈Hbase之宏观架构
相关文章 五分钟轻松了解Hbase列式存储 Hbase给初学者的“下马威” Hbase王国游记之:Hbase客户端API初体验
- 关于利用maven搭建ssm的博客,我们一起来探讨下问的最多的问题
前言 开心一刻 有个同学去非洲援建,刚到工地接待他的施工员是个黑人,他就用英语跟人家交流,黑人没做声. 然后他又用法语,黑人还是没说话. 然后他用手去比划.黑人终于开口了:瞎比划嘎哈,整个工地都中国人 ...
- 史上最走心webpack4.0中级教程——配置之外你应该知道的事
<webpack4.0各个击破系列>适合不满足于只会配置webpack但一时间又看不懂源码的中级读者.我没法保证这个系列是最好的,但至少能保证每一篇博文都跟那些Ctrl+C和Ctrl+V的 ...
- HTTP 常见的状态码
- vb.net C# AtlAxGetControl 函数使用方法
先省略一万字的坑坑坑 找遍了互联网既然没搜索到相关资料 一定是我搜索的方法有问题 下面简单几句代码 第一种方法 Public Declare Function AtlAxGetControl Lib ...
- web中的——作者也不知道这里面写的啥
1.使用超链接<a href="/Default/Index">添加</a><a href="#">返回显示信息</a ...
- [Go] golang连接查询mysql
package main import ( "database/sql" _ "github.com/go-sql-driver/mysql" "lo ...
- ejs常用语法
nodejs的模板引擎有很多, ejs是比较简单和容易上手的.常用的一些语法: 用<%...%>包含js代码 用<%=...%>输出变量 变量若包含 '<' '>' ...
- JavaScript splice() 方法和JavaScript split() 方法
定义和用法 splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目. 注释:该方法会改变原始数组. 语法 arrayObject.splice(index,howmany,item1, ...
- SQL Challenges
平台: http://www.zixem.altervista.org/SQLi/ Level 1 (Super Easy) http://www.zixem.altervista.org/SQLi/ ...