接下来为BNRDrawView添加单击手势,让用户可以选择屏幕上的线条(后面章节会添加删除选中线条的功能)。这次仍然使用UITapGestureRecognizer对象,但是点击次数需要改为1。(UITapGestureRecognizer的numberOfTapsRequired属性默认值就是1,不需要在代码中设置。)
在BNRDrawView.m中,修改initWithFrame:方法,代码如下:
[self addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *tapRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(tap:)];
tapRecognizer.delaysTouchesBegan = YES;
[self addGestureRecognizer:tapRecognizer];
}
return self;
}
然后实现tap:方法,当UITapGestureRecognizer识别出单击手势时,会向控制台输出一条日志信息。
- (void)tap:(UIGestureRecognizer *)gr
{
NSLog(@“Recognized tap”);
}
构建并运行应用。可以发现,点击一次可以正确识别出单击手势,控制台会输出tap:中的日志信息;但是,如果点击两次,BNRDrawView无法区分单击手势和双击手势,tap:和doubleTap:都会执行。
如果需要为视图添加多种手势,就需要考虑这些手势之间的关系。双击手势包含两次单击,为了避免UITapGestureRecognizer将双击事件分拆为两个单击事件,可以设置UITapGestureRecognizer在单击后暂时不进行识别,直到确定不是双击手势后再识别为单击手势。
在initWithFrame:中添加以下代码:
UITapGestureRecognizer *tapRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(tap:)];
tapRecognizer.delaysTouchesBegan = YES;
[tapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
[self addGestureRecognizer:tapRecognizer];
构建并运行应用,单击屏幕,UITapGestureRecognizer会稍作停顿,确定是单击手势后再执行tap:方法;而双击屏幕不会再执行tap:方法了。
现在为BNRDrawView添加单击选择线条功能。首先在BNRDrawView.m的类扩展中添加一个属性,用于保存选中的线条,代码如下:
@interface BNRDrawView ()
@property (nonatomic, strong) NSMutableDictionary *linesInProgress;
@property (nonatomic, strong) NSMutableArray *finishedLines;
@property (nonatomic, weak) BNRLine *selectedLine;
@end
(请读者注意,以上代码将selectedLine设置为弱引用,原因是:首先,finishedLines数组会保存selectedLine,是强引用;其次,如果用户清除所有线条,finishedLines会移除selectedLine,这时BNRDrawView会自动将selectedLine设置为nil。)
下面在drawRect:中添加代码,用绿色绘制选中的线条:
[[UIColor redColor] set];
for (NSValue *key in self.linesInProgress) {
[self strokeLine:self.linesInProgress[key]];
}
if (self.selectedLine) {
[[UIColor greenColor] set];
[self strokeLine:self.selectedLine];
}
然后在BNRDrawView.m中实现lineAtPoint:,根据传入的位置找出距离最近的那个BNRLine对象,代码如下:
- (BNRLine *)lineAtPoint:(CGPoint)p
{
// 找出离p最近的BNRLine对象
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);
// 如果线条的某个点和p的距离在20点以内,就返回相应的BNRLine对象
if (hypot(x - p.x, y - p.y) < 20.0) {
return l;
}
}
}
// 如果没能找到符合条件的线条,就返回nil,代表不选择任何线条
return nil;
}
(要找到距离某个点最近的线条,还有更好的算法,以上的lineAtPoint:只是一个简化的实现。)
运行TouchTracker时,需要向lineAtPoint:传入手势点击的位置。通过UIGestureRecognizer对象,可以很容易地获取该信息。UIGestureRecognizer对象有一个名为locationInView:的方法,该方法会根据传入的UIView对象的坐标系返回手势发生时的位置信息。
在BNRDrawView.m的tap:中先调用UIGestureRecognizer对象的locationInView:,然后将得到的位置信息作为实参传给lineAtPoint:,最后将返回的BNRLine对象赋给selectedLine。
- (void)tap:(UIGestureRecognizer *)gr
{
NSLog(@“Recognized tap”);
CGPoint point = [gr locationInView:self];
self.setSelectedLine = [self lineAtPoint:point];
[self setNeedsDisplay];
}
构建并运行应用。先画一些线条,然后点击其中的某根线条,TouchTracker应该会用绿色重绘这根线条。