Objective-c – Right way to implement TouchesMoved


I have a custom UIView that generates a set of subviews and display them in rows and columns like tiles. What I am trying to achieve is to allow the user to touch the screen and as the finger move, the tiles beneath it disappears.

The code below is the custom UIView that contains the tiles:

- (id)initWithFrame:(CGRect)frame {

    if (self = [super initWithFrame:frame]) {
        int i, j;
        int maxCol = floor(self.frame.size.width/TILE_SPACING);
        int maxRow = floor(self.frame.size.height/TILE_SPACING);

        CGRect frame = CGRectMake(0, 0, TILE_WIDTH, TILE_HEIGHT);
        UIView *tile;       

        for (i = 0; i<maxCol; i++) {
            for (j = 0; j < maxRow; j++) {
                frame.origin.x =  i * (TILE_SPACING) + TILE_PADDING;
                frame.origin.y =  j * (TILE_SPACING) + TILE_PADDING;
                tile = [[UIView alloc] initWithFrame:frame];

                [self addSubview:tile];
                [tile release]; 

    return self;

- (void)touchesBegan: (NSSet *)touches withEvent:(UIEvent *)event {
    UIView *tile = [self hitTest:[[touches anyObject] locationInView:self] withEvent:nil];

    if (tile != self)
        [tile setHidden:YES];

- (void)touchesMoved: (NSSet *)touches withEvent:(UIEvent *)event {
    UIView *tile = [self hitTest:[[touches anyObject] locationInView:self] withEvent:nil];

    if (tile != self)
        [tile setHidden:YES];

This approach works but however if the tiles get denser (i.e. small tiles and more tiles on the screen). The iPhone is less responsive as the finger move. It may be the hitTest taking a toll on the processor as it struggles to keep up but would like some opinions.

My questions are:

  1. Is this an efficient way / right way to implement the touchesMoved?

  2. If it isn't, what would be the recommended approach?

  3. I tried moving the functionality into a custom Tile class (a sub UIView) which the class above would create and add as a subview. This subview Tile can handle the TouchesBegan but as the finger move, the other tiles does not receive the TouchesBegan even since the touches are still part of the initial touch sequence. Is there a way to implement it through the subview Tile class, how do other tiles receive the TouchesBegan/TouchesMoved event as the finger move?

Best Solution

  1. Not really, in my experience.
  2. From the way you've set up your views, there would seem to be a much more efficient way to determine which one was tapped:

 //In your init method, make sure each tile doesn't respond to clicks on its own
 tile.userInteractionEnabled = NO;

 - (void) touchesMoved: (NSSet *)touches withEvent:(UIEvent *)event {
      CGPoint tappedPt = [[touches anyObject] locationInView: self];
      int     xPos = tappedPt.x / (TILE_SPACING + TILE_PADDING);
      int     yPos = tappedPt.y / (TILE_SPACING + TILE_PADDING);
      int     tilesAcross = (self.bounds.size.width / (TILE_SPACING + TILE_PADDING));
      int     index = xPos + yPos * tilesAcross;

      if (index < self.subviews.count) {
          UIView  *tappedTile = [self.subviews objectAtIndex: index];
          tappedTile.hidden = YES;

  1. You need to more clearly decide what should happen in your parent, and what should happen in each tile. You mentioned above that you wanted the tiles to receive touchesBegan events, but you didn't include any code for this. As you presented this, you should probably think about using layers instead of views (if you're not doing any tile-specific event handling). What you've got here can all be done with event tracking within the parent. Keep in mind that if you have TOO many views (or layers), it's gonna get slow. We tried grids of 15x15 in Crosswords, and while it was okay for some things, eventually we gave up on the grid-of-layers approach.

(Not sure why the numbering restarted with 1 here...)

Related Question