Chart Annotation with UIButton - target / selector not firing


#1

I’m adding an SChartAnnotation to a chart, with a UIButton as a subview. It shows up fine, however clicking the button does not fire the target-action. The same code works as expected outside of the annotation. I tried setting userInteractionEnabled = YES on both the annotation and my subview, but that didn’t help. Any ideas? 


#2

Hi Justin, 
  
Yeah, this is something that the ShinobiCharts framework doesn’t currently support out of the box. We’ll add a story into our product backlog to see if we should support this on the framework API. For the meantime, I’ve created a workaround which should help you get going. 
  
 By default, a chart will swallow any taps on it and handle them itself. However, you can set up your view controller so that you can add further tap gesture recognizers to the chart. First, you need to update your view controller so that it adopts the UIGestureRecognizerDelegate protocol. Then, in your implementation, add the following delegate method to allow gesture recognizers to analyze their gestures simultaneously: 

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 
  return YES; 
 }

  
 Now, you can add another tap recognizer to your chart, using the following code snippet: 
  

UITapGestureRecognizer *tapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)] autorelease]; 
tapRecognizer.delegate = self; 
[self.chart addGestureRecognizer:tapRecognizer];

  
 Because we have added the view controller as a delegate on the new tap recognizer, it will analyze taps simultaneously with the normal recognizers on the chart. 
  
 In the code below, I’ve added an annotation onto the chart, which contains a button. This method creates the annotation: 
  

- (SChartAnnotation *)createAnnotation { 
   SChartAnnotation *annotation = [[[SChartAnnotation alloc] initWithFrame:CGRectMake(0, 0, 200, 200)] autorelease]; 
   annotation.backgroundColor = [UIColor colorWithWhite:0.4f alpha:0.4f]; 
  
   self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
   [self.button setTitle:@"Press Me" forState:UIControlStateNormal]; 
   [self.button addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside]; 
   self.button.frame = CGRectMake(0, 0, 100, 50); 
   self.button.center = annotation.center; 
  
   self.button.autoresizingMask = ~UIViewAutoresizingNone; 
  
   [annotation addSubview:self.button]; 
  
   return annotation; 
 }   

 And I’ve added the annotation to the chart with the following code snippet: 

self.annotation = [self createAnnotation]; 
[self.chart addAnnotation:self.annotation];   

 Using our custom tap recognizer, we can forward taps on from the chart to our annotation. In our tap handler method, we evaluate whether the tap occurred on the button in the annotation, and then mock the button being pressed if it did: 
  

- (void)handleTap:(UITapGestureRecognizer*)sender { 
    CGPoint locationOfTap = [sender locationInView:self.annotation]; 
    if (CGRectContainsPoint(self.button.frame, locationOfTap)) { 
       self.button.highlighted = YES; 
       [self.button sendActionsForControlEvents:UIControlEventTouchUpInside]; 
       [self performSelector:@selector(unhighlightButton) withObject:nil afterDelay:0.1]; 
    } 
 } 
  
 - (void)unhighlightButton { 
    self.button.highlighted = NO; 
 }

 Hopefully this example should give you a possible way to get around the issue you have seen.
  
 Many thanks, 
  
 Dan


#3

Yep, this put me on the right track. Thank you!


#4

Got this working for myself. Just had a related query. How do I reposition my annotation to a specific position on the screen. right hand top corner or left hand top corner of the screen.


#5

@ShinobiSquad - is this workaround still required?  I have created an annotation on a chart that can be dragged along the x axis.  When I enable the zooming and panning on the chart, the annotation dragging stops working.

Do I need to implement a pan gesture recognizer on the chart similar to the tap gesture recognizer above?  This would be slightly different as the chart would need to block panning if the annotation was touched, so that only the annotation moved (and the chart didn’t pan).

Thanks.


#6

Hi Guys,

kapsym - you should be able to set the annotation’s .center property, which should reposition it correctly :slight_smile:

Tim - the issue seems to be that the chart’s tap gesture recogniser was intercepting the touches first, and the fix was to allow the annotation recogniser to run with this, simultaneously. Adding the same simultaneous delegate to your drag/zoom gesture recogniser should allow it to recognise gestures on the annotation, however, as you mentioned, if the annotation recogniser is running along with the chart recogniser, there may be some strange UI effects. You would have to call requiresGestureRecognizerToFail: on the chart’s gesture recogniser. To access this you will need to import SChartCanvas and SChartOverlay, and using the following category should allow you to do that:

@interface SChartCanvasOverlay ()

  • (UIGestureRecognizer *)chartPanRecognizer
    {
        return (UIGestureRecognizer *)pinchAndPanGesture;
    }

@end

Hope this helps!
Rob


#7

Thank you - will give that a go!