Multiple Gesture Recognizers

Let’s add another gesture recognizer that allows the user to select a line. (Later, a user will be able to delete the selected line.) You will install another UITapGestureRecognizer on the BNRDrawView that only requires one tap.

In BNRDrawView.m, modify initWithFrame:.

 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​a​d​d​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​:​d​o​u​b​l​e​T​a​p​R​e​c​o​g​n​i​z​e​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​T​a​p​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​ ​*​t​a​p​R​e​c​o​g​n​i​z​e​r​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​[​U​I​T​a​p​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​T​a​r​g​e​t​:​s​e​l​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​c​t​i​o​n​:​@​s​e​l​e​c​t​o​r​(​t​a​p​:​)​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​t​a​p​R​e​c​o​g​n​i​z​e​r​.​d​e​l​a​y​s​T​o​u​c​h​e​s​B​e​g​a​n​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​a​d​d​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​:​t​a​p​R​e​c​o​g​n​i​z​e​r​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

Now, implement tap: to log the tap to the console in BNRDrawView.m.

-​ ​(​v​o​i​d​)​t​a​p​:​(​U​I​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​ ​*​)​g​r​
{​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​R​e​c​o​g​n​i​z​e​d​ ​t​a​p​"​)​;​
}​

Build and run the application. Tapping once will log the appropriate message to the console. The only problem, however, is that tapping twice will trigger both tap: and doubleTap:.

In situations where you have multiple gesture recognizers, it is not uncommon to have a gesture recognizer fire when you really want another gesture recognizer to handle the work. In these cases, you set up dependencies between recognizers that say, Just wait a moment before you fire, because this gesture might be mine!

In initWithFrame:, make it so the tapRecognizer must wait for the doubleTapRecognizer to fail before it can assume that a single tap is not just the first of a double tap.

U​I​T​a​p​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​ ​*​t​a​p​R​e​c​o​g​n​i​z​e​r​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​[​U​I​T​a​p​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​T​a​r​g​e​t​:​s​e​l​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​c​t​i​o​n​:​@​s​e​l​e​c​t​o​r​(​t​a​p​:​)​]​;​
t​a​p​R​e​c​o​g​n​i​z​e​r​.​d​e​l​a​y​s​T​o​u​c​h​e​s​B​e​g​a​n​ ​=​ ​Y​E​S​;​
[​t​a​p​R​e​c​o​g​n​i​z​e​r​ ​r​e​q​u​i​r​e​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​T​o​F​a​i​l​:​d​o​u​b​l​e​T​a​p​R​e​c​o​g​n​i​z​e​r​]​;​
[​s​e​l​f​ ​a​d​d​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​:​t​a​p​R​e​c​o​g​n​i​z​e​r​]​;​

Build and run the application. A single tap now takes a small amount of time to fire after the tap occurs, but double-tapping no longer triggers the tap: message.

Now, let’s build on the BNRDrawView so that the user can select lines when they are tapped. First, add a property to hold onto a selected line to the class extension in BNRDrawView.m.

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​D​r​a​w​V​i​e​w​ ​(​)​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​s​t​r​o​n​g​)​ ​N​S​M​u​t​a​b​l​e​D​i​c​t​i​o​n​a​r​y​ ​*​l​i​n​e​s​I​n​P​r​o​g​r​e​s​s​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​s​t​r​o​n​g​)​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​f​i​n​i​s​h​e​d​L​i​n​e​s​;​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​w​e​a​k​)​ ​B​N​R​L​i​n​e​ ​*​s​e​l​e​c​t​e​d​L​i​n​e​;​

@​e​n​d​

(Notice that this property is weak: the finishedLines array will hold the strong reference to the line and selectedLine will be set to nil if the line is removed from finishedLines by clearing the screen.)

Now, in drawRect:, add some code to the bottom of the method to draw the selected line in green.

 ​ ​ ​ ​[​[​U​I​C​o​l​o​r​ ​r​e​d​C​o​l​o​r​]​ ​s​e​t​]​;​
 ​ ​ ​ ​f​o​r​ ​(​N​S​V​a​l​u​e​ ​*​k​e​y​ ​i​n​ ​s​e​l​f​.​l​i​n​e​s​I​n​P​r​o​g​r​e​s​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​s​t​r​o​k​e​L​i​n​e​:​s​e​l​f​.​l​i​n​e​s​I​n​P​r​o​g​r​e​s​s​[​k​e​y​]​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​.​s​e​l​e​c​t​e​d​L​i​n​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​U​I​C​o​l​o​r​ ​g​r​e​e​n​C​o​l​o​r​]​ ​s​e​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​s​t​r​o​k​e​L​i​n​e​:​s​e​l​f​.​s​e​l​e​c​t​e​d​L​i​n​e​]​;​
 ​ ​ ​ ​}​
}​

Implement lineAtPoint: in BNRDrawView.m to get a BNRLine close to the given point.

-​ ​(​B​N​R​L​i​n​e​ ​*​)​l​i​n​e​A​t​P​o​i​n​t​:​(​C​G​P​o​i​n​t​)​p​
{​
 ​ ​ ​ ​/​/​ ​F​i​n​d​ ​a​ ​l​i​n​e​ ​c​l​o​s​e​ ​t​o​ ​p​
 ​ ​ ​ ​f​o​r​ ​(​B​N​R​L​i​n​e​ ​*​l​ ​i​n​ ​s​e​l​f​.​f​i​n​i​s​h​e​d​L​i​n​e​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​C​G​P​o​i​n​t​ ​s​t​a​r​t​ ​=​ ​l​.​b​e​g​i​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​C​G​P​o​i​n​t​ ​e​n​d​ ​=​ ​l​.​e​n​d​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​h​e​c​k​ ​a​ ​f​e​w​ ​p​o​i​n​t​s​ ​o​n​ ​t​h​e​ ​l​i​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​f​l​o​a​t​ ​t​ ​=​ ​0​.​0​;​ ​t​ ​<​=​ ​1​.​0​;​ ​t​ ​+​=​ ​0​.​0​5​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​l​o​a​t​ ​x​ ​=​ ​s​t​a​r​t​.​x​ ​+​ ​t​ ​*​ ​(​e​n​d​.​x​ ​-​ ​s​t​a​r​t​.​x​)​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​l​o​a​t​ ​y​ ​=​ ​s​t​a​r​t​.​y​ ​+​ ​t​ ​*​ ​(​e​n​d​.​y​ ​-​ ​s​t​a​r​t​.​y​)​;​

 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​I​f​ ​t​h​e​ ​t​a​p​p​e​d​ ​p​o​i​n​t​ ​i​s​ ​w​i​t​h​i​n​ ​2​0​ ​p​o​i​n​t​s​,​ ​l​e​t​'​s​ ​r​e​t​u​r​n​ ​t​h​i​s​ ​l​i​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​h​y​p​o​t​(​x​ ​-​ ​p​.​x​,​ ​y​ ​-​ ​p​.​y​)​ ​<​ ​2​0​.​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​l​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​I​f​ ​n​o​t​h​i​n​g​ ​i​s​ ​c​l​o​s​e​ ​e​n​o​u​g​h​ ​t​o​ ​t​h​e​ ​t​a​p​p​e​d​ ​p​o​i​n​t​,​ ​t​h​e​n​ ​w​e​ ​d​i​d​ ​n​o​t​ ​s​e​l​e​c​t​ ​a​ ​l​i​n​e​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​i​l​;​
}​

(There are better ways to implement lineAtPoint:, but this simplistic implementation is OK for your current purpose.)

The point you are interested in, of course, is where the tap occurred. You can easily get this information. Every UIGestureRecognizer has a locationInView: method. Sending this message to the gesture recognizer will give you the coordinate where the gesture occurred in the coordinate system of the view that is passed as the argument.

In BNRDrawView.m, send the locationInView: message to the gesture recognizer, pass the result to lineAtPoint:, and make the returned line the selectedLine.

-​ ​(​v​o​i​d​)​t​a​p​:​(​U​I​G​e​s​t​u​r​e​R​e​c​o​g​n​i​z​e​r​ ​*​)​g​r​
{​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​R​e​c​o​g​n​i​z​e​d​ ​t​a​p​"​)​;​

 ​ ​ ​ ​C​G​P​o​i​n​t​ ​p​o​i​n​t​ ​=​ ​[​g​r​ ​l​o​c​a​t​i​o​n​I​n​V​i​e​w​:​s​e​l​f​]​;​
 ​ ​ ​ ​s​e​l​f​.​s​e​l​e​c​t​e​d​L​i​n​e​ ​=​ ​[​s​e​l​f​ ​l​i​n​e​A​t​P​o​i​n​t​:​p​o​i​n​t​]​;​

 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​N​e​e​d​s​D​i​s​p​l​a​y​]​;​
}​

Build and run the application. Draw a few lines and then tap on one. The tapped line should appear in green, but remember that it takes a short moment before the tap is known not to be a double tap.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset