Turning Touches into Lines

A line is defined by two points. Your BNRLine stores these points as properties named begin and end. When a touch begins, you will create a line and set both begin and end to the point where the touch began. When the touch moves, you will update end. When the touch ends, you will have your complete line.

In BNRDrawView.m, implement touchesBegan:withEvent: to create a new line.

-​ ​(​v​o​i​d​)​t​o​u​c​h​e​s​B​e​g​a​n​:​(​N​S​S​e​t​ ​*​)​t​o​u​c​h​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​E​v​e​n​t​:​(​U​I​E​v​e​n​t​ ​*​)​e​v​e​n​t​
{​
 ​ ​ ​ ​U​I​T​o​u​c​h​ ​*​t​ ​=​ ​[​t​o​u​c​h​e​s​ ​a​n​y​O​b​j​e​c​t​]​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​l​o​c​a​t​i​o​n​ ​o​f​ ​t​h​e​ ​t​o​u​c​h​ ​i​n​ ​v​i​e​w​'​s​ ​c​o​o​r​d​i​n​a​t​e​ ​s​y​s​t​e​m​
 ​ ​ ​ ​C​G​P​o​i​n​t​ ​l​o​c​a​t​i​o​n​ ​=​ ​[​t​ ​l​o​c​a​t​i​o​n​I​n​V​i​e​w​:​s​e​l​f​]​;​

 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​ ​=​ ​[​[​B​N​R​L​i​n​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​.​b​e​g​i​n​ ​=​ ​l​o​c​a​t​i​o​n​;​
 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​.​e​n​d​ ​=​ ​l​o​c​a​t​i​o​n​;​

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

Then, in BNRDrawView.m, implement touchesMoved:withEvent: so that it updates the end of the currentLine.


-​ ​(​v​o​i​d​)​t​o​u​c​h​e​s​M​o​v​e​d​:​(​N​S​S​e​t​ ​*​)​t​o​u​c​h​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​E​v​e​n​t​:​(​U​I​E​v​e​n​t​ ​*​)​e​v​e​n​t​
{​
 ​ ​ ​ ​U​I​T​o​u​c​h​ ​*​t​ ​=​ ​[​t​o​u​c​h​e​s​ ​a​n​y​O​b​j​e​c​t​]​;​
 ​ ​ ​ ​C​G​P​o​i​n​t​ ​l​o​c​a​t​i​o​n​ ​=​ ​[​t​ ​l​o​c​a​t​i​o​n​I​n​V​i​e​w​:​s​e​l​f​]​;​

 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​.​e​n​d​ ​=​ ​l​o​c​a​t​i​o​n​;​

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

Finally, in BNRDrawView.m, add the currentLine to the finishedLines when the touch ends.


-​ ​(​v​o​i​d​)​t​o​u​c​h​e​s​E​n​d​e​d​:​(​N​S​S​e​t​ ​*​)​t​o​u​c​h​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​E​v​e​n​t​:​(​U​I​E​v​e​n​t​ ​*​)​e​v​e​n​t​
{​
 ​ ​ ​ ​[​s​e​l​f​.​f​i​n​i​s​h​e​d​L​i​n​e​s​ ​a​d​d​O​b​j​e​c​t​:​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​]​;​

 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​ ​=​ ​n​i​l​;​

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

Build and run the application and draw some lines on the screen. While you are drawing, the lines will appear in red and once finished, they will appear in black.

Handling multiple touches

When drawing lines, you may have noticed that having more than one finger on the screen does not do anything – that is, you can only draw one line at a time. Let’s update BNRDrawView so that you can draw as many lines as you can fit fingers on the screen.

By default, a view will only accept one touch at a time. If one finger has already triggered touchesBegan:withEvent: but has not finished – and therefore has not triggered touchesEnded:withEvent: – subsequent touches are ignored. In this context, ignore means that the BNRDrawView will not be sent touchesBegan:withEvent: or any other UIResponder messages related to the extra touches.

In BNRDrawView.m, enable BNRDrawView instances to accept multiple touches.

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​F​r​a​m​e​:​(​C​G​R​e​c​t​)​r​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​r​]​;​

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​f​i​n​i​s​h​e​d​L​i​n​e​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​b​a​c​k​g​r​o​u​n​d​C​o​l​o​r​ ​=​ ​[​U​I​C​o​l​o​r​ ​g​r​a​y​C​o​l​o​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​m​u​l​t​i​p​l​e​T​o​u​c​h​E​n​a​b​l​e​d​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​}​

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

Now that BNRDrawView will accept multiple touches, each time a finger touches the screen, moves, or is removed from the screen, the view will receive the appropriate UIResponder message. However, this now presents a problem: your UIResponder code assumes there will only be one touch active and one line being drawn at a time.

Notice, first, that each touch handling method you have already implemented sends the message anyObject to the NSSet of touches it receives. In a single-touch view, there will only ever be one object in the set, so asking for any object will always give you the touch that triggered the event. In a multiple touch view, that set could contain more than one touch.

Then, notice that there is only one property (currentLine) that hangs on to a line in progress. Obviously, you will need to hold as many lines as there are touches currently on the screen. While you could create a few more properties, like currentLine1 and currentLine2, you would have to go to considerable lengths to manage which instance variable corresponds to which touch.

Instead of the multiple property approach, you can use an NSMutableDictionary to hang on to each BNRLine in progress. The key to store the line in the dictionary will be derived from the UITouch object that the line corresponds to. As more touch events occur, you can use the same algorithm to derive the key from the UITouch that triggered the event and use it to look up the appropriate BNRLine in the dictionary.

In BNRDrawView.m, add a new instance variable to replace the currentLine and instantiate it in initWithFrame:.

@​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​)​ ​B​N​R​L​i​n​e​ ​*​c​u​r​r​e​n​t​L​i​n​e​;​
@​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​;​

@​e​n​d​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​N​R​D​r​a​w​V​i​e​w​

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​F​r​a​m​e​:​(​C​G​R​e​c​t​)​r​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​F​r​a​m​e​:​r​]​;​

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​l​i​n​e​s​I​n​P​r​o​g​r​e​s​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​D​i​c​t​i​o​n​a​r​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​f​i​n​i​s​h​e​d​L​i​n​e​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​b​a​c​k​g​r​o​u​n​d​C​o​l​o​r​ ​=​ ​[​U​I​C​o​l​o​r​ ​g​r​a​y​C​o​l​o​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​m​u​l​t​i​p​l​e​T​o​u​c​h​E​n​a​b​l​e​d​ ​=​ ​Y​E​S​;​
 ​ ​ ​ ​}​

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

Now you need to update the UIResponder methods to add lines that are currently being drawn to this dictionary. In BNRDrawView.m, update the code in touchesBegan:withEvent:.

-​ ​(​v​o​i​d​)​t​o​u​c​h​e​s​B​e​g​a​n​:​(​N​S​S​e​t​ ​*​)​t​o​u​c​h​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​E​v​e​n​t​:​(​U​I​E​v​e​n​t​ ​*​)​e​v​e​n​t​
{​
 ​ ​ ​ ​/​/​ ​L​e​t​'​s​ ​p​u​t​ ​i​n​ ​a​ ​l​o​g​ ​s​t​a​t​e​m​e​n​t​ ​t​o​ ​s​e​e​ ​t​h​e​ ​o​r​d​e​r​ ​o​f​ ​e​v​e​n​t​s​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​N​S​S​t​r​i​n​g​F​r​o​m​S​e​l​e​c​t​o​r​(​_​c​m​d​)​)​;​

 ​ ​ ​ ​f​o​r​ ​(​U​I​T​o​u​c​h​ ​*​t​ ​i​n​ ​t​o​u​c​h​e​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​C​G​P​o​i​n​t​ ​l​o​c​a​t​i​o​n​ ​=​ ​[​t​ ​l​o​c​a​t​i​o​n​I​n​V​i​e​w​:​s​e​l​f​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​B​N​R​L​i​n​e​ ​*​l​i​n​e​ ​=​ ​[​[​B​N​R​L​i​n​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​l​i​n​e​.​b​e​g​i​n​ ​=​ ​l​o​c​a​t​i​o​n​;​
 ​ ​ ​ ​ ​ ​ ​ ​l​i​n​e​.​e​n​d​ ​=​ ​l​o​c​a​t​i​o​n​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​V​a​l​u​e​ ​*​k​e​y​ ​=​ ​[​N​S​V​a​l​u​e​ ​v​a​l​u​e​W​i​t​h​N​o​n​r​e​t​a​i​n​e​d​O​b​j​e​c​t​:​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​l​i​n​e​s​I​n​P​r​o​g​r​e​s​s​[​k​e​y​]​ ​=​ ​l​i​n​e​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​U​I​T​o​u​c​h​ ​*​t​ ​=​ ​[​t​o​u​c​h​e​s​ ​a​n​y​O​b​j​e​c​t​]​;​
 ​ ​ ​ ​C​G​P​o​i​n​t​ ​l​o​c​a​t​i​o​n​ ​=​ ​[​t​ ​l​o​c​a​t​i​o​n​I​n​V​i​e​w​:​s​e​l​f​]​;​

 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​ ​=​ ​[​[​B​N​R​L​i​n​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​.​b​e​g​i​n​ ​=​ ​l​o​c​a​t​i​o​n​;​
 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​.​e​n​d​ ​=​ ​l​o​c​a​t​i​o​n​;​

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

First, notice that you use fast enumeration to loop over all of the touches that began, because it is possible that more than one touch can begin at the same time. (Although typically touches begin at different times and BNRDrawView will receive multiple touchesBegan:withEvent: messages containing each touch.)

Next, notice the use of valueWithNonretainedObject: to derive the key to store the BNRLine. This method creates an NSValue instance that holds on to the address of the UITouch object that will be associated with this line. Since a UITouch is created when a touch begins, updated throughout its lifetime, and destroyed when the touch ends, the address of that object will be constant through each touch event message.

Figure 12.4  Object diagram for Multitouch TouchTracker

Object diagram for Multitouch TouchTracker

Update touchesMoved:withEvent: in BNRDrawView.m so that it can look up the right BNRLine.

-​ ​(​v​o​i​d​)​t​o​u​c​h​e​s​M​o​v​e​d​:​(​N​S​S​e​t​ ​*​)​t​o​u​c​h​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​E​v​e​n​t​:​(​U​I​E​v​e​n​t​ ​*​)​e​v​e​n​t​
{​
 ​ ​ ​ ​/​/​ ​L​e​t​'​s​ ​p​u​t​ ​i​n​ ​a​ ​l​o​g​ ​s​t​a​t​e​m​e​n​t​ ​t​o​ ​s​e​e​ ​t​h​e​ ​o​r​d​e​r​ ​o​f​ ​e​v​e​n​t​s​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​N​S​S​t​r​i​n​g​F​r​o​m​S​e​l​e​c​t​o​r​(​_​c​m​d​)​)​;​

 ​ ​ ​ ​f​o​r​ ​(​U​I​T​o​u​c​h​ ​*​t​ ​i​n​ ​t​o​u​c​h​e​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​V​a​l​u​e​ ​*​k​e​y​ ​=​ ​[​N​S​V​a​l​u​e​ ​v​a​l​u​e​W​i​t​h​N​o​n​r​e​t​a​i​n​e​d​O​b​j​e​c​t​:​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​B​N​R​L​i​n​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​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​l​i​n​e​.​e​n​d​ ​=​ ​[​t​ ​l​o​c​a​t​i​o​n​I​n​V​i​e​w​:​s​e​l​f​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​U​I​T​o​u​c​h​ ​*​t​ ​=​ ​[​t​o​u​c​h​e​s​ ​a​n​y​O​b​j​e​c​t​]​;​
 ​ ​ ​ ​C​G​P​o​i​n​t​ ​l​o​c​a​t​i​o​n​ ​=​ ​[​t​ ​l​o​c​a​t​i​o​n​I​n​V​i​e​w​:​s​e​l​f​]​;​

 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​.​e​n​d​ ​=​ ​l​o​c​a​t​i​o​n​;​

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

Then, update touchesEnded:withEvent: to move any finished lines into the _finishedLines array.

-​ ​(​v​o​i​d​)​t​o​u​c​h​e​s​E​n​d​e​d​:​(​N​S​S​e​t​ ​*​)​t​o​u​c​h​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​E​v​e​n​t​:​(​U​I​E​v​e​n​t​ ​*​)​e​v​e​n​t​
{​
 ​ ​ ​ ​/​/​ ​L​e​t​'​s​ ​p​u​t​ ​i​n​ ​a​ ​l​o​g​ ​s​t​a​t​e​m​e​n​t​ ​t​o​ ​s​e​e​ ​t​h​e​ ​o​r​d​e​r​ ​o​f​ ​e​v​e​n​t​s​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​N​S​S​t​r​i​n​g​F​r​o​m​S​e​l​e​c​t​o​r​(​_​c​m​d​)​)​;​

 ​ ​ ​ ​f​o​r​ ​(​U​I​T​o​u​c​h​ ​*​t​ ​i​n​ ​t​o​u​c​h​e​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​V​a​l​u​e​ ​*​k​e​y​ ​=​ ​[​N​S​V​a​l​u​e​ ​v​a​l​u​e​W​i​t​h​N​o​n​r​e​t​a​i​n​e​d​O​b​j​e​c​t​:​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​B​N​R​L​i​n​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​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​.​f​i​n​i​s​h​e​d​L​i​n​e​s​ ​a​d​d​O​b​j​e​c​t​:​l​i​n​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​.​l​i​n​e​s​I​n​P​r​o​g​r​e​s​s​ ​r​e​m​o​v​e​O​b​j​e​c​t​F​o​r​K​e​y​:​k​e​y​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​[​s​e​l​f​.​f​i​n​i​s​h​e​d​L​i​n​e​s​ ​a​d​d​O​b​j​e​c​t​:​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​]​;​

 ​ ​ ​ ​s​e​l​f​.​c​u​r​r​e​n​t​L​i​n​e​ ​=​ ​n​i​l​;​

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

Finally, update drawRect: to draw each line in _linesInProgress.

 ​ ​ ​ ​/​/​ ​D​r​a​w​ ​f​i​n​i​s​h​e​d​ ​l​i​n​e​s​ ​i​n​ ​b​l​a​c​k​
 ​ ​ ​ ​[​[​U​I​C​o​l​o​r​ ​b​l​a​c​k​C​o​l​o​r​]​ ​s​e​t​]​;​
 ​ ​ ​ ​f​o​r​ ​(​B​N​R​L​i​n​e​ ​*​l​i​n​e​ ​i​n​ ​s​e​l​f​.​f​i​n​i​s​h​e​d​L​i​n​e​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​s​t​r​o​k​e​L​i​n​e​:​l​i​n​e​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​
 ​ ​ ​ ​[​[​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​.​c​u​r​r​e​n​t​L​i​n​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​D​r​a​w​ ​l​i​n​e​ ​i​n​ ​p​r​o​g​r​e​s​s​ ​i​n​ ​r​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​U​I​C​o​l​o​r​ ​r​e​d​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​.​c​u​r​r​e​n​t​L​i​n​e​]​;​
 ​ ​ ​ ​}​
}​

Build and run the application and start drawing lines with multiple fingers. (You can simulate multiple fingers on the simulator by holding down the option key as you drag.)

You may be wondering: why not use the UITouch itself as the key? Why go through the hoop of creating an NSValue? Objects used as keys in an NSDictionary must conform to the NSCopying protocol, which allows them to be copied by sending the message copy. UITouch instances do not conform to this protocol because it does not make sense for them to be copied. Thus, the NSValue instances hold the address of the UITouch so that equal NSValue instances can be later created with the same UITouch.

Also, you should know that when a UIResponder message like touchesMoved:withEvent: is sent to a view, only the touches that have moved will be in the NSSet of touches. Thus, it is possible for three touches to be on a view, but only one touch inside the set of touches passed into one of these methods if the other two did not move. Additionally, once a UITouch begins on a view, all touch event messages are sent to that same view over the touch’s lifetime, even if that touch moves off of the view it began on.

The last thing left for the basics of TouchTracker is to handle what happens when a touch is cancelled. A touch can be cancelled when an application is interrupted by the operating system (for example, a phone call comes in) when a touch is currently on the screen. When a touch is cancelled, any state it set up should be reverted. In this case, you should remove any lines in progress.

In BNRDrawView.m, implement touchesCancelled:withEvent:.


-​ ​(​v​o​i​d​)​t​o​u​c​h​e​s​C​a​n​c​e​l​l​e​d​:​(​N​S​S​e​t​ ​*​)​t​o​u​c​h​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​i​t​h​E​v​e​n​t​:​(​U​I​E​v​e​n​t​ ​*​)​e​v​e​n​t​
{​
 ​ ​ ​ ​/​/​ ​L​e​t​'​s​ ​p​u​t​ ​i​n​ ​a​ ​l​o​g​ ​s​t​a​t​e​m​e​n​t​ ​t​o​ ​s​e​e​ ​t​h​e​ ​o​r​d​e​r​ ​o​f​ ​e​v​e​n​t​s​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​%​@​"​,​ ​N​S​S​t​r​i​n​g​F​r​o​m​S​e​l​e​c​t​o​r​(​_​c​m​d​)​)​;​

 ​ ​ ​ ​f​o​r​ ​(​U​I​T​o​u​c​h​ ​*​t​ ​i​n​ ​t​o​u​c​h​e​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​V​a​l​u​e​ ​*​k​e​y​ ​=​ ​[​N​S​V​a​l​u​e​ ​v​a​l​u​e​W​i​t​h​N​o​n​r​e​t​a​i​n​e​d​O​b​j​e​c​t​:​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​.​l​i​n​e​s​I​n​P​r​o​g​r​e​s​s​ ​r​e​m​o​v​e​O​b​j​e​c​t​F​o​r​K​e​y​:​k​e​y​]​;​
 ​ ​ ​ ​}​

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

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

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