For gestures that do not easily match a specific pattern, or when you want to use a gesture recognizer to gather touch input, create a continuous gesture recognizer.
A continuous gesture recognizer lets you encapsulate your event-handling logic in one place and reuse that logic in multiple views. Although continuous gesture recognizers require a little more effort to implement the state machine, they also perform tasks that would be difficult with a discrete gesture recognizer, such as capturing free-form input.
Figure 1 shows a free-form gesture whose input you might use to draw paths onscreen. Although you could use a pan gesture recognizer to capture the input, your action method would need to handle all of the phases of the capture process, which would add to its complexity. Using a custom gesture recognizer, you can simplify your code by distributing your logic to various methods of your subclass. Using a custom gesture recognizer also means that you can write your code for capturing the path once and reuse it in multiple views.
For a custom gesture recognizer that captures touch input, there are no explicit conditions that trigger a failure of the gesture. Instead, the gesture recognizer captures touch input until the touch sequence ends or is cancelled by the system. While the gesture is ongoing, the gesture recognizer places the touch data into a temporary buffer. Clients of the gesture recognizer use their action method to fetch that buffer and apply it temporarily to the app’s content. For example, a client might use that data to draw the path onscreen. Only when the touch sequence ends successfully would those target objects commit the data permanently to the app’s data structures.
Saving Gesture-Related Data
A continuous gesture recognizer that tracks touch events needs a way to store that information. You cannot simply store references to the
UITouch objects that you receive because UIKit reuses those objects and overwrites any old values. Instead, you must define custom data structures to store the touch information you need.
Listing 1 shows the definition of a
Stroke struct, whose purpose is to store the location associated with a touch. In your own implementation, you might add other properties to this struct to store information such as the timestamp or the force of the touch.
Listing 2 shows the partial definition of a
Touch class used to capture touch information. This class stores touch data in the
samples property, which is an array of
Stroke structs. The class also stores the
UITouch object associated with the first finger so that it can ignore any other touches. The implementation of the
init(coder:) method ensures that the
samples property is initialized properly when loading the gesture recognizer from an Interface Builder file.
Processing Touch Events
Listing 3 shows the
touches method of the
Touch class. The gesture fails immediately if the initial event contains two touches. If there is only one touch, the touch object is saved in the
tracked property and the custom
add helper method creates a new
Stroke struct with the touch data. After the first touch occurs, any new touches added to the event sequence are ignored.
touches methods (shown in Listing 4) record each new sample and update the gesture recognizer’s state. Setting the state to
ended is equivalent to setting the state to
recognized and results in a call to the gesture recognizer’s action method.
Resetting the Gesture Recognizer
Always implement the
touches and [
reset()] methods in your gesture recognizers and use them to perform any cleanup. Listing 5 shows the implementation of these methods for the
Touch class. Both methods restore the gesture recognizer’s properties to their initial values.