Incorporating Gesture Support

You can add greater interactivity to your collection views through the use of gesture recognizers. Add a gesture recognizer to a collection view, and use it to trigger actions when those gestures occur. For a collection view, there are two types of actions that you might likely want to implement:

You should always attach your gesture recognizers to the collection view itself—not to a specific cell or view. The UICollectionView class is a descendant of UIScrollView, so attaching your gesture recognizers to the collection view is less likely to interfere with the other gestures that must be tracked. In addition, because the collection view has access to your data source and your layout object, you still have access to all the information you need to manipulate cells and views appropriately.

Using a Gesture Recognizer to Modify Layout Information

A gesture recognizer offers an easy way to modify layout parameters dynamically. For example, you might use a pinch gesture recognizer to change the spacing between items in a custom layout. The process for configuring such a gesture recognizer is relatively simple.

  1. Create the gesture recognizer.

  2. Attach the gesture recognizer to the collection view.

  3. Use the handler method of the gesture recognizer to update the layout parameters and invalidate the layout object.

You create a gesture recognizer using the same alloc/init process that you use for all objects. During initialization, you specify the target object and action method to call when the gesture is triggered. You then call the collection view’s addGestureRecognizer: method to attach it to the view. Most of the actual work happens in the action method you specify at initialization time.

Listing 4-1 shows an example of an action method that is called by a pinch gesture recognizer attached to a collection view. In this example, the pinch data is used to change the distance between cells in a custom layout. The layout object implements the custom updateSpreadDistance method, which validates the new distance value and stores it for use during the layout process later. The action method then invalidates the layout and forces it to update the position of items based on the new value.

Listing 4-1  Using a gesture recognizer to change layout values

- (void)handlePinchGesture:(UIPinchGestureRecognizer *)sender {
    if ([sender numberOfTouches] != 2)
        return;
 
   // Get the pinch points.
   CGPoint p1 = [sender locationOfTouch:0 inView:[self collectionView]];
   CGPoint p2 = [sender locationOfTouch:1 inView:[self collectionView]];
 
   // Compute the new spread distance.
    CGFloat xd = p1.x - p2.x;
    CGFloat yd = p1.y - p2.y;
    CGFloat distance = sqrt(xd*xd + yd*yd);
 
   // Update the custom layout parameter and invalidate.
   MyCustomLayout* myLayout = (MyCustomLayout*)[[self collectionView] collectionViewLayout];
   [myLayout updateSpreadDistance:distance];
   [myLayout invalidateLayout];
}

For more information about creating gesture recognizers and attaching them to views, see Event Handling Guide for iOS.

Working with Default Gesture Behaviors

The UICollectionView class listens for single taps to initiate its delegate methods for highlighting and selecting. If you want to add custom tap or long-press gestures to a collection view, configure the values of your gesture recognizer to be different than the values the collection view already uses. For example, you might configure a tap gesture recognizer to respond only to double-taps.

Listing 4-2 shows how you might make the collection view respond to your gesture instead of listening for cell selection/highlighting. Because the collection view does not use a gesture recognizer to initiate its delegate methods, your custom gesture recognizer gets priority over the default selection listeners by delaying the registering of other touch events by setting the delaysTouchesBegan property of your gesture recognizer to YES or cancelling touch events by setting the cancelsTouchesInView property of your gesture recognizer to YES. Whenever a tap is registered, it will first check to see if your gesture recognizer should have priority or not. If the input is not valid for your gesture recognizer, then the delegate methods will be called as normal.

Listing 4-2  Prioritizing your gesture recognizer

UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
tapGesture.delaysTouchesBegan = YES;
tapGesture.numberOfTapsRequired = 2;
[self.collectionView addGestureRecognizer:tapGesture];

Manipulating Cells and Views

How you use a gesture recognizer to manipulate cells and views depends on the types of manipulations you plan to make. Simple insertions and deletions can be performed inside the action method of a standard gesture recognizer. But if you plan more complex manipulations, you probably need to define a custom gesture recognizer to track the touch events yourself.

One type of manipulation that requires a custom gesture recognizer to move a cell in your collection view from one location to another. The most straightforward way to move a cell is to delete it (temporarily) from the collection view, use the gesture recognizer to drag around a visual representation of that cell, and then insert the cell at its new location when the touch events end. All of this requires managing the touch events yourself, working closely with the layout object to determine the new insertion location, manipulating the data source changes, and then inserting the item at the new location.

For more information about creating custom gesture recognizers, see Event Handling Guide for iOS.