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 ...
随机推荐
- Linux rsync 两个目录镜像备份
rsync安装篇 rsync是一款配置简单,功能全面的安全备份软件,具体的功能介绍可以参考手册.这里和大家分享一下rsync在CentOS下的部署. 1.安装rsync,并通过xinetd管理rsyn ...
- 第一册:lesson 111.
原文:The most expensive model. question:Can Mr.Frith buy the television on instalments? How does it wo ...
- Maven(九)Eclipse创建Web项目(简单方式)
1. 创建Maven项目(以简单方式) 2. 勾选WAR 3. 选择properties->projectFacts 此处的错误可忽略,配置好会会消失,主要缺失web.xml文件 4. 将框中选 ...
- es6 Symbol类型
es6 新增了一个原始类型Symbol,代表独一无二的数据 javascript 原来有6中基本类型, Boolean ,String ,Object,Number, null , undefined ...
- js中函数表达式和自执行函数表达式的用法总结
立即调用函数表达式 给函数体加大括号,在有变量声明的情形下,没有任何区别 但是,如果只是[自动执行]的情形下,就会不同 因为,一个匿名函数,不赋值或函数体不加小括号,是不能自动执行的 //以下情形并无 ...
- vue生成二维码插件qrcodejs2
1.页面 <div id="qrCode" ref="qrCodeDiv"></div> 2.导入插件 import QRCode fr ...
- Oracle11g: datetime
--上一月,上一年 select add_months(sysdate,-1) last_month,add_months(sysdate,-12) last_year from dual; --下一 ...
- bitset中_Find_first()与_Find_next()函数
bitset中_Find_first()与_Find_next()函数 很有趣但是没怎么有用的两个函数. _Find_fisrt就是找到从低位到高位第一个1的位置 #include<bits/s ...
- html标签种类很多,为什么不都用div?
why not divs? 所有html页面标签都可以用div解决,为什么还会存在各种不同的标签呢? 代码是写给机器阅读的,初始化标签更利于快速编程,毕竟很多标签有了自定义属性,无需编码控制,可维护性 ...
- 2019-02-18 扩展Python控制台实现中文反馈信息之二-正则替换
"中文编程"知乎专栏原文地址 续前文扩展Python控制台实现中文反馈信息, 实现了如下效果: >>> 学 Traceback (most recent call ...