首页 » iOS编程(第4版) » iOS编程(第4版)全文在线阅读

《iOS编程(第4版)》13.3 同时添加多种触摸手势

关灯直达底部

接下来为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应该会用绿色重绘这根线条。