Skip to content

I 5.6. Handling Events in Collection Views

rayastar edited this page Feb 14, 2015 · 2 revisions

Problem

You want to be able to handle collection view events, such as taps.

Solution

Assign a delegate to your collection view. In some other cases, you may not even have to do that. All you may need to do is listen for those events in your cell classes and handle them right there.

Collection views have delegate properties that have to conform to the UICollection ViewDelegate protocol. The delegate object will then receive various delegation calls from the collection view informing the delegate of various events, such as a cell be‐ coming highlighted or selected. You need to know the difference between the highligh‐ ted and selected state of a collection view cell. When the user presses her finger down on a cell in a collection view but doesn’t lift her finger up, the cell under her finger is highlighted. When she presses her finger down and lifts her finger up to say she wants to perform an action on the cell, that cell will then be selected.

Collection view cells of type UICollectionViewCell have two very useful properties, highlighted and selected, that get set to YES when the cell becomes highlighted or selected.

If all you want to do is change your cell’s visual presentation when it becomes selected, you’re in luck, because cells of type UICollectionViewCell expose a property named selectedBackgroundView of type UIView that you can set to a valid view. This view will then get displayed on the screen once your cell becomes selected. Let’s demonstrate this by building on top of what we created in Recipe 5.5. If you remember, in that example, we created a custom cell that had a background image view property named image ViewBackgroundImage, which covered the whole of the cell. We were loading custom image instances into that image view. What we want now is to set the background color of our cell to blue once the cell becomes selected. Because the image view is sitting on top of everything else on our collection view, before we set the background color of our cell, we need to ensure that our image view is see-through by changing the background color of the image view to a clear color. The reason behind this is that an image view is opaque by default, so if you place it on a view that has a background color, you won’t be able to see the color of the view because the opaque image view will not be see- through. Thus, in order for us to see the color of our image view’s super view, we will set the image view’s background color to a clear and see-through color. So let’s get started:

#import "MyCollectionViewCell.h"

@implementation MyCollectionViewCell

- (void) awakeFromNib{
    [super awakeFromNib];
    self.imageViewBackgroundImage.backgroundColor = [UIColor clearColor];
    self.selectedBackgroundView = [[UIView alloc] initWithFrame:self.bounds];
    self.selectedBackgroundView.backgroundColor = [UIColor whiteColor];
}

@end

That’s all, really! Now if you tap on any of the cells in your program, you will see that the background color of the cell becomes blue.

There are more things that you may want to do with your collection view by listening to various events that it sends. For instance, you may want to play a sound or an ani‐ mation once a cell becomes selected. Let’s say, when the user taps on a cell, that we want to use an animation to hide the cell momentarily and then show it again, creating a fading-out and fading-in animation. If this is the type of thing you want to do, start by setting the delegate object of your collection view, because that’s really where you get a lot of events reported back to you. Your delegate object, as mentioned before, has to conform to the UICollectionViewDelegate protocol. This protocol contains a lot of useful methods that you can implement. The following are some of the most important methods in this protocol:

collectionView:didHighlightItemAtIndexPath: Gets called on the delegate when a cell becomes highlighted.

collectionView:didUnhighlightItemAtIndexPath: Gets called on the delegate when a cell comes out of the highlighted state. This method gets called when the user successfully finishes the tap event (pushes her finger on the item and lifts her finger off it, generating the tap gesture) or it can get called if the user cancels her earlier highlighting of the cell by dragging her finger out of the boundaries of the cell.

collectionView:didSelectItemAtIndexPath: This method gets called on the delegate object when a given cell becomes selected. The cell is always highlighted before it is selected.

collectionView:didDeselectItemAtIndexPath: Gets called on the delegate object when the cell comes out of the selected state.

So let’s build an app according to our earlier requirements. We want to fade out the cell and fade it back in when it becomes selected. In your UICollectionViewController instance, implement the collectionView:didSelectItemAtIndexPath: method like so:

#import "ViewController.h"
#import "MyCollectionViewCell.h"

static NSString *kCollectionViewCellIdentifier = @"Cells";
const NSTimeInterval kAnimationDuration = 0.20;

@implementation ViewController

- (void)        collectionView:(UICollectionView *)collectionView
   didHighlightItemAtIndexPath:(NSIndexPath *)indexPath{

    UICollectionViewCell *selectedCell =
        [collectionView cellForItemAtIndexPath:indexPath];

    [UIView animateWithDuration:kAnimationDuration animations:^{
        selectedCell.transform = CGAffineTransformMakeScale(2.0f, 2.0f);
    }];

}

OK! That was easy. How about another example? Let’s say you want to make a cell twice as big as its normal size when it becomes highlighted and then take it back to its original size when it loses its highlighted state. That means, when the user presses her finger down on the cell (before releasing her finger), the cell enlarges to twice its size and then, when she releases her finger, the cell goes back to its original size. For this, we have to implement the collectionView:didHighlightItemAtIndexPath: and the collection View:didUnhighlightItemAtIndexPath: methods of the UICollectionViewDele gate protocol in our collection view controller (remember, collection view controllers, by default, conform to the UICollectionViewDelegate and the UICollectionViewDataSource protocols):

- (void)        collectionView:(UICollectionView *)collectionView
 didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath{

    UICollectionViewCell *selectedCell =
        [collectionView cellForItemAtIndexPath:indexPath];

    [UIView animateWithDuration:kAnimationDuration animations:^{
        selectedCell.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
    }];

}

- (void)    collectionView:(UICollectionView *)collectionView
  didSelectItemAtIndexPath:(NSIndexPath *)indexPath{

    UICollectionViewCell *selectedCell =
        [collectionView cellForItemAtIndexPath:indexPath];

    const NSTimeInterval kAnimationDuration = 0.20;

    [UIView animateWithDuration:kAnimationDuration animations:^{
        selectedCell.alpha = 0.0f;
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:kAnimationDuration animations:^{
            selectedCell.alpha = 1.0f;
        }];
    }];
}

As you can see, we are using the CGAffineTransformMakeScale Core Graphics function to create an affine transformation, and then assigning that to the cell itself to create the visual effect of the cell growing twice as large, before shrinking back to its original size.

Clone this wiki locally