title: How do I limit touch handling to one layer when layers overlap?
body: <p>I have an interesting problem handling touch events in a cocos2D program I’m writing.  I have 3 CCLayer sublassed layers in a CCScene:</p>
  
  <p>backgroundLayer – z:0 – simple static layer used to display a background image.</p>
  
  <p>planetLayer - z:3 – display layer – visualization of data changes are displayed here.</p>
  
  <p>gameControlsLayer – z:5 – layer used to display data controllers such as sliders and buttons.</p>
  
  <p>I separated the planet and control layers because I want to pan and zoom the planet layer without worrying about the controls being affected.</p>
  
  <p>Here is the scene init code that sets up these layers:</p>
  
  <pre><code>- (id) init
  {
      self = [super init];
      if (self != nil)
      {
          // background layer
          BackgroundLayer *backgroundLayer = [BackgroundLayer node];
          [self addChild:backgroundLayer z:0];
  
          // planet layer
          PlanetLayer *planetLayer = [PlanetLayer node];
          [self addChild:planetLayer z:3];
  
          // gameplay layer
          GameControlsLayer *gameControlsLayer = [GameControlsLayer node];
          [self addChild:gameControlsLayer z:5];
      }
      return self;
  }
  </code></pre>
  
  <p>I need touch handling on both the planet and control layers. On the control layer, the touch handling is inside control objects that are layer children. I wrote all this code first and it works fine. </p>
  
  <p>After that, I wrote layer-wide touch handling for the planet layer so I could do touch scrolling and zooming. I’ve got the scrolling in (though not the zoom yet), and it works fine. My problem is this: When I touch and manipulate the controls on the control layer, they work fine, but as I move a slider thumb up and down, for example, the planet layer behind the control layer pans as if the touches were meant for it.</p>
  
  <p>It may be redundant to include the code, but here are the relevant snippets:</p>
  
  <p>For the slider control, a child of the control layer:</p>
  
  <pre><code>- (void) onEnterTransitionDidFinish
  {
      [[CCTouchDispatcher sharedDispatcher] 
        addTargetedDelegate:self priority:1 swallowsTouches:YES];
  }
  
  - (void) onExit
  {
      [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
  }
  
  #pragma mark Touch Delegate
  
  - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
  {
      CGPoint location = [[CCDirector sharedDirector]
                 convertToGL:[touch locationInView:[touch view]]];
  
  
  
      float distance = [self distanceBetweenPointOne:thumbPosition
                                         andPointTwo:location];
      if (distance &gt; thumbRadius*2)
      {
          return NO;
      }
  
      // touch is on the thumb. store the location so we 
      // can calculate changes on ccTouchMoved events
      touched = YES;
      return YES;
  }
  
  - (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
  {
      if (!touched)
      {
          return;
      }
  
      CGPoint location = [[CCDirector sharedDirector]
                 convertToGL:[touch locationInView:[touch view]]];
      location = [self convertToNodeSpace:location];
  
      // if we're too far off the thumb, abandon it
      float distance = [self distanceBetweenPointOne:thumbPosition
                                         andPointTwo:location];
      if (distance &gt; thumbRadius*3)
      {
          //touched = NO;
          return;
      }
  
      // this call adjusts some ivars – not relevant
      [self updateValueAndPosition:location];
  }
  
  - (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
  {
      if (touched)
      {
          touched = NO;
      }
  }
  
  - (void)ccTouchCancelled:(UITouch *)touch 
                 withEvent:(UIEvent *)event
  {
      [self ccTouchEnded:touch withEvent:event];
  }
  </code></pre>
  
  <p>Here is the relevant code from the planet layer:</p>
  
  <pre><code>// Yes, we want touch notifications please
  //
  - (void) onEnterTransitionDidFinish
  {
      [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
         priority:0 swallowsTouches:NO];
  }
  
  #pragma mark Touch Delegate
  
  - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
  {
      CGPoint touchLocation = [touch locationInView:[touch view]];
      touchLocation = [[CCDirector sharedDirector]
                        convertToGL:touchLocation];
  
      // ****NOTE – the following is a kludge I used it to break
      // ********** the behavior; the controls are all grouped
      // ********** at the bottom of the screen. But this is not
      // ********** an acceptable fix. I shouldn’t need to mask
      // ********** off certain areas of the screen. This strategy
      // ********** will not hold once I add other controls to the
      // ********** control layer.
      //
      if (touchLocation.y &gt; 250.0f)
      {
          self.touched = YES;
          touchHash = [touch hash];
          return YES;
      }
      return YES;
  }
  
  - (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
  {
      if (!touched)
      {
          return;
      }
  
      if ([touch hash] == touchHash)
      {
          CGSize screenSize = [[CCDirector sharedDirector] winSize];
  
          CGPoint touchLocation = 
                  [touch locationInView:[touch view]];
          CGPoint prevLocation = 
                  [touch previousLocationInView:[touch view]];
  
          touchLocation = [[CCDirector sharedDirector]
                            convertToGL:touchLocation];
          prevLocation = [[CCDirector sharedDirector]
                            convertToGL:prevLocation];
  
          CGPoint diff = ccpSub(touchLocation, prevLocation);
  
          CGPoint currentPosition = [self position];
          CGPoint newPosition = ccpAdd(currentPosition, diff);
  
          if (newPosition.x &lt; lowestX)
              newPosition.x = lowestX;
          else if (newPosition.x &gt; highestX-screenSize.width)
              newPosition.x = highestX-screenSize.width;
  
          if (newPosition.y &lt; lowestY)
              newPosition.y = lowestY;
          else if (newPosition.y &gt; highestY-screenSize.height)
              newPosition.y = highestY-screenSize.height;
  
          [self setPosition:newPosition];
      }
  }
  
  - (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
  {
      if ([touch hash] == touchHash)
      {
          if (touched)
          {
              touched = NO;
          }
      }
  }
  
  - (void)ccTouchCancelled:(UITouch *)touch 
                 withEvent:(UIEvent *)event
  {
      [self ccTouchEnded:touch withEvent:event];
  }
  </code></pre>
  
  <p>I want to add the pinch zoom capabilities, but I need to clear up this issue before that will be advisable. Does anybody have the answer here? I feel like I’m missing something obvious…</p>
  
  <p>Thanks!</p>