What I would like to accomplish
I am using Core Plot (1.1) to draw a bar chart and I would like to present a popover with further details below the bar which has been selected (tapped) by the user.
Code
My code looks like this:
- (void)barPlot:(CPTBarPlot *)plot barWasSelectedAtRecordIndex:(NSUInteger)idx {
NSNumber *yValue = self.values[idx];
NSLog(@"barWasSelectedAtRecordIndex x: %i, y: %@",idx,yValue);
NSDecimal plotPoint[2];
NSNumber *plotXvalue = [self numberForPlot:plot
field:CPTScatterPlotFieldX
recordIndex:idx];
plotPoint[CPTCoordinateX] =
CPTDecimalFromFloat(plotXvalue.floatValue);
NSNumber *plotYvalue = [self numberForPlot:plot
field:CPTScatterPlotFieldY
recordIndex:idx];
plotPoint[CPTCoordinateY] =
CPTDecimalFromFloat(plotYvalue.floatValue);
CPTXYPlotSpace *plotSpace =
(CPTXYPlotSpace *)graph.defaultPlotSpace;
CGPoint dataPoint =
[plotSpace plotAreaViewPointForPlotPoint:plotPoint];
NSLog(@"datapoint (CGPoint) coordinates tapped: %@",
NSStringFromCGPoint(dataPoint));
GrowthChartInfoTableViewController *infoViewController =
[self.storyboard instantiateViewControllerWithIdentifier:
@"GrowthChartInfo"];
self.popover = [[UIPopoverController alloc]
initWithContentViewController:infoViewController];
self.popover.popoverContentSize = CGSizeMake(320, 300);
self.popover.delegate = self;
CGRect popoverAnchor =
CGRectMake(dataPoint.x + graph.paddingLeft,
dataPoint.y - graph.paddingTop + graph.paddingBottom,
(CGFloat)1.0f, (CGFloat)1.0f);
[self.popover presentPopoverFromRect:popoverAnchor
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
My problem
The y-position of the popover view controller is incorrect, it is different for each bar and for the first bar, which has a negative y-value, it is presented at the upper limit of the bar instead the lower limit and hides the bar. The x-position seems to be correct.
Please help
Any ideas on why my code does not work as expected?
Thank you!
Edit
Based on Eric's answer I have updated my code as follows:
- (void)barPlot:(CPTBarPlot *)plot barWasSelectedAtRecordIndex:(NSUInteger)idx {
NSNumber *plotXvalue = [self numberForPlot:plot
field:CPTScatterPlotFieldX
recordIndex:idx];
NSNumber *plotYvalue = [self numberForPlot:plot
field:CPTScatterPlotFieldY
recordIndex:idx];
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)graph.defaultPlotSpace;
CGPoint cgPlotPoint =
CGPointMake(plotXvalue.floatValue,
plotYvalue.floatValue);
CGPoint cgPlotAreaPoint =
[graph convertPoint:cgPlotPoint toLayer:graph.plotAreaFrame.plotArea];
NSDecimal plotAreaPoint[2];
plotAreaPoint[CPTCoordinateX] =
CPTDecimalFromFloat(cgPlotAreaPoint.x);
plotAreaPoint[CPTCoordinateY] =
CPTDecimalFromFloat(cgPlotAreaPoint.y);
CGPoint dataPoint = [plotSpace
plotAreaViewPointForPlotPoint:plotAreaPoint];
NSLog(@"datapoint (CGPoint) coordinates tapped: %@",NSStringFromCGPoint(dataPoint));
GrowthChartInfoTableViewController *infoViewController = [self.storyboard
instantiateViewControllerWithIdentifier:@"GrowthChartInfo"];
self.popover = nil;
self.popover = [[UIPopoverController alloc]
initWithContentViewController:infoViewController];
self.popover.popoverContentSize = CGSizeMake(250, 200);
self.popover.delegate = self;
CGRect popoverAnchor =
CGRectMake(dataPoint.x + graph.paddingLeft,
dataPoint.y - graph.paddingTop + graph.paddingBottom,
(CGFloat)1.0f, (CGFloat)1.0f);
[self.popover presentPopoverFromRect:popoverAnchor
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
}
However, the results I am getting are more erroneous than before. I believe I must have misunderstand something. Here are some of the results of the NSLog commands:
barWasSelectedAtRecordIndex x: 0, y: -0.03920931548773198848
datapoint (CGPoint) coordinates tapped: {-6388.88, -67024.8}
barWasSelectedAtRecordIndex x: 2, y: 0.174494288286651392
datapoint (CGPoint) coordinates tapped: {-6073.38, -66790}
barWasSelectedAtRecordIndex x: 2, y: 0.174494288286651392
datapoint (CGPoint) coordinates tapped: {-6073.38, -66790}
barWasSelectedAtRecordIndex x: 0, y: -0.03920931548773198848
datapoint (CGPoint) coordinates tapped: {-6388.88, -67024.8}
It seems that the conversion from the bar coordinates to the view coordinates is completely wrong in my code.
Edit 2
Thank you again for your code sample, Eric!
I have updated my code and tested it with the following options (y-value = 0: plotPoint[CPTCoordinateY] = CPTDecimalFromFloat(0.0f)
, which results in an anchor point for the popover whose y-value seems to be incorrect (please refer to the following screenshot). The anchor point's y-value should be equivalent to the position of 0
on the y-axis.
Then I tried to set the y-value of the anchor point to the current y-value of the data. (plotPoint[CPTCoordinateY] = plotYvalue.decimalValue
). This results in an anchor point which is close to the y-axis's 0 value instead of being close to the top of the bar. However, the y-value changes a little bit for each company (x-value) indicating that there might be a problem with the shift of the y-values:
I also tried the additional conversion which Eric suggested dataPoint = [self.view convertPoint:dataPoint fromView:graph.hostingView]
, but this did not change the results. In addition, I tried to add - graph.paddingTop + graph.paddingBottom
to the final y-values, without success.
I would appreciate any ideas on what my error might be.
Thank you!
The
-plotAreaViewPointForPlotPoint:
method returns a point in the plot area coordinate system. You need to convert that to the graph's coordinate system:The graph and its hosting view share a coordinate system. If
self.view
is not the hosting view, you will need to convert the coordinates to its coordinate system from the hosting view.A more complete code sample: