Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 351
Ronnie Liew Avatar asked Oct 18 '08 18:10

Ronnie Liew


3 Answers

  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...)

like image 97
Ben Gottlieb Avatar answered Oct 20 '22 03:10

Ben Gottlieb


one addition, instead of hit testing, you can just check if the point lies in the CGRect that represents the frame of each subview. I have a similar app, and this worked best for me.

for (UIView* aSubview in self.subviews) {

    if([aSubview pointInside: [self convertPoint:touchPoint toView:aSubview] withEvent:event]){

       //Do stuff
    }
}
like image 36
Corey Floyd Avatar answered Oct 20 '22 02:10

Corey Floyd


Just an idea that may help... I have not tested this, but, after detecting the tap on a particular imageview turn off userInteractionEnabled of the all other imageviews...

I suppose this will help increase a little bit the speed, as iPhone will not last resources trying to find which image is being tapped during a drag. The same is true for multitouch.

like image 1
Duck Avatar answered Oct 20 '22 01:10

Duck