继续上篇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的更多相关文章

  1. iOS UIGestureRecognizer与UIMenuController(内容根据iOS编程)

    UIGestureRecognizer 对象会截取本应由视图处理的触摸事件.当某个UIGestureRecognizer对象识别出特定的手势后,就会向指定的对象发送指定的消息.iOS SDK默认提供若 ...

  2. iOS 手势识别器(UIGestureRecognizer)

    UIGestureRecognizer是一个抽象类,定义了所有手势的基本行为,使用它的子类才能处理具体的手势. UIGestureRecognizer的子类有: UITapGestureRecogni ...

  3. 手势(UIGestureRecognizer)

    通过继承 UIGestureRecognizer 类,实现自定义手势(手势识别器类). PS:自定义手势时,需要 #import <UIKit/UIGestureRecognizerSubcla ...

  4. UIKit框架之UIGestureRecognizer

    ---恢复内容开始--- 1.继承链:NSObject 2.UIGestureRecognizer的子类有以下: UITapGestureRecognizer :点击 UIPinchGestureRe ...

  5. 你真的了解UIGestureRecognizer吗?

    一:首先查看一下关于UIGestureRecognizer的定义 //当前手势状态 typedef NS_ENUM(NSInteger, UIGestureRecognizerState) { //尚 ...

  6. UIGestureRecognizer

    •为了完成手势识别,必须借助于手势识别器----UIGestureRecognizer • •利用UIGestureRecognizer,能轻松识别用户在某个view上面做的一些常见手势 • •UIG ...

  7. IOS手势UIGestureRecognizer

    UIGestureRecognizer是一个抽象类,定义了所有手势的基本行为,它有6个子类处理具体的手势: 1.UITapGestureRecognizer (任意手指任意次数的点击) // 点击次数 ...

  8. iOS基础篇(十七)——UIGestureRecognizer用法

    UIGestureRecognizer(手势识别)在iOS 中非常重要,他极大地提高了移动设备的使用便捷性: 在3.2之前是主要使用的是由UIResponder而来的如下4种方式: - (void)t ...

  9. 点击事件touches与ios的手势UIGestureRecognizer

    .h文件 @property (weak,nonatomic) IBOutlet UILabel *messageLabel;@property (weak,nonatomic) IBOutlet U ...

随机推荐

  1. Java架构师趣谈Hbase之宏观架构

    相关文章 五分钟轻松了解Hbase列式存储 Hbase给初学者的“下马威” Hbase王国游记之:Hbase客户端API初体验

  2. 关于利用maven搭建ssm的博客,我们一起来探讨下问的最多的问题

    前言 开心一刻 有个同学去非洲援建,刚到工地接待他的施工员是个黑人,他就用英语跟人家交流,黑人没做声. 然后他又用法语,黑人还是没说话. 然后他用手去比划.黑人终于开口了:瞎比划嘎哈,整个工地都中国人 ...

  3. 史上最走心webpack4.0中级教程——配置之外你应该知道的事

    <webpack4.0各个击破系列>适合不满足于只会配置webpack但一时间又看不懂源码的中级读者.我没法保证这个系列是最好的,但至少能保证每一篇博文都跟那些Ctrl+C和Ctrl+V的 ...

  4. HTTP 常见的状态码

  5. vb.net C# AtlAxGetControl 函数使用方法

    先省略一万字的坑坑坑 找遍了互联网既然没搜索到相关资料 一定是我搜索的方法有问题 下面简单几句代码 第一种方法 Public Declare Function AtlAxGetControl Lib ...

  6. web中的——作者也不知道这里面写的啥

    1.使用超链接<a href="/Default/Index">添加</a><a href="#">返回显示信息</a ...

  7. [Go] golang连接查询mysql

    package main import ( "database/sql" _ "github.com/go-sql-driver/mysql" "lo ...

  8. ejs常用语法

    nodejs的模板引擎有很多, ejs是比较简单和容易上手的.常用的一些语法: 用<%...%>包含js代码 用<%=...%>输出变量 变量若包含 '<' '>' ...

  9. JavaScript splice() 方法和JavaScript split() 方法

    定义和用法 splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目. 注释:该方法会改变原始数组. 语法 arrayObject.splice(index,howmany,item1, ...

  10. SQL Challenges

    平台: http://www.zixem.altervista.org/SQLi/ Level 1 (Super Easy) http://www.zixem.altervista.org/SQLi/ ...