Crosshair with line only to the X axis


#1

Hi,

Is it possible to customize the behavior of the crosshair so instead of drawing lines to both X and Y axes, it does only to the X axis? i.e. I just want a vertical line from the current point to the X axis of my chart.

I have gone through the documentation and the closest I could found is:

-(BOOL)shouldDrawCrosshairLinesForPoint:(CGPoint)point inFrame:(CGRect)frame

But that just prevents both lines to be displayed for selected points.


#2

Update: Posted a new solution for this on the second page, code changes mean this solution needs tweaking in the latest release - Matt W (20/01/2016)

Hi - to make any changes to the drawing of the crosshair you need to draw a new crosshair yourself. To do this you need to subclass SChartCrosshair and override the drawCrosshairLines method to perform your own drawing.

Since the drawCrosshairLines method doesn’t give you any drawing information, you need to store any information you need about the crosshair as ivars so you can use them within your drawCrosshairLines implementation.

Find below an implementation of an SChartCrosshair subclass that draws a single vertical line for your crosshair:

@implementation CustomCrosshair {
    SChartPoint _crosshairPoint;
}


-(void)drawCrosshairLines
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Line start point.
    CGContextMoveToPoint(context,_crosshairPoint.x, 0.f);

    // Line end point.
    CGContextAddLineToPoint(context,_crosshairPoint.x, self.chart.canvas.glView.frame.size.height);

    CGContextStrokePath(context);
}

-(void)moveToPosition:(SChartPoint)coords andDisplayDataPoint:(SChartPoint)dataPoint fromSeries:(SChartCartesianSeries *)series andSeriesDataPoint:(id<SChartData>)dataseriesPoint
{
    [super moveToPosition:coords andDisplayDataPoint:dataPoint fromSeries:series andSeriesDataPoint:dataseriesPoint];
    
    // Store the point where the crosshair should be draw.
    _crosshairPoint = coords;
}

@end

Note: You will have to import SChartCanvas to get access to the glView (so you can find out the frame of your plot area & draw accordingly).

I hope the above helps! Let me know if you have any more questions.

Jan


#3

Imported SChartCanvas and getting .glView still produces an error. Any suggestions?


#4

Hi,

Thanks for getting in touch!

It may be that your are missing an import of SChartGLView.

Could you provide the error log that is produced?

Kind regards,
Andrew Polkinghorn


#5

I have used the code mentioned above by @jan . I cannot get the crosshair from x, i dont even get any crosshair. Please help me out


#6

I got issue resolved. Thanks.


#7

Is this possible to override in the current Android SDK?  It doesn’t look like I can extend the Crosshair class.  


#8

Hi leewoods,

You are correct that in the Android version of  shinobicharts you are not able to extend the Crosshair class. Instead we provide a callback interface called  ShiobiChart.OnCrosshairDrawListener. An implementation of this can be set on your chart to draw your crosshair in a customised way.

Take a look at the API docs for more information.

Kind regards,

Patrick


#9

Thanks for the reply Patrick.  I will implement it using the interface.


#10

Thanks for the reply Patrick.  I will implement it using the interface.


#11

Thanks for the reply Patrick.  I will implement it using the interface.


#12

Update: this will no longer work due to changes that were made to SChartCrosshair in a previous release.

You can still achieve it by subclassing SChartCrosshair but the implementation has changed slightly.

class CustomCrosshair: SChartCrosshair
{
    var crosshairPoint = SChartPoint()
    let crosshairLineLayer = CAShapeLayer()
    
    override init!(chart parentChart: ShinobiChart!)
    {
        super.init(chart: parentChart)
        self.layer.insertSublayer(crosshairLineLayer, atIndex: 0)
    }

    required init?(coder aDecoder: NSCoder)
    {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func drawCrosshairLines()
    {
        let rect = self.chart.getPlotAreaFrame()
        let yAxisLinePath = UIBezierPath()
        yAxisLinePath.moveToPoint(CGPointMake(CGFloat(crosshairPoint.x), CGFloat(crosshairPoint.y)))
        yAxisLinePath.addLineToPoint(CGPointMake(CGFloat(crosshairPoint.x), rect.origin.y + rect.size.height))
        
        crosshairLineLayer.strokeColor = self.style.lineColor.CGColor
        crosshairLineLayer.lineWidth = CGFloat(self.style.lineWidth)
        crosshairLineLayer.path = yAxisLinePath.CGPath
    }
    
    override func moveToPosition(coords: SChartPoint, andDisplayDataPoint dataPoint: SChartPoint, fromSeries series: SChartMappedSeries!, andSeriesDataPoint dataseriesPoint: SChartData!) {
        crosshairPoint = coords
        super.moveToPosition(coords, andDisplayDataPoint: dataPoint, fromSeries: series, andSeriesDataPoint: dataseriesPoint)
    }
}

#13

Hi Matt,

Hope all is going well mate.

Thanks for the update, I was just looking for this just yesterday - timing! :slight_smile:

I also found the previous example didn’t work, the graphics context was always coming back zero. I was able to get it to work with the following code:

-(void)moveToPosition:(SChartPoint)coords andDisplayDataPoint:(SChartPoint)dataPoint fromSeries:(SChartCartesianSeries *)series andSeriesDataPoint:(id<SChartData>)dataseriesPoint {
    _crosshairPoint = coords;
    [super moveToPosition:coords andDisplayDataPoint:dataPoint fromSeries:series andSeriesDataPoint:dataseriesPoint];
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(context,_crosshairPoint.x, 0.f);
    CGContextAddLineToPoint(context, _crosshairPoint.x, self.chart.canvas.glView.frame.size.height);
    CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
    CGContextStrokePath(context);
}

-(void)drawCrosshairLines {
    [self setNeedsDisplay];
}

(my example has a full height axis) 

I have tried your version above with the shape layer and also can confirm it works well, but wanted to let you know there is a bug in that drawCrosshairLines appears to be called as part of the super call to moveToPosition. This means the crosshairPoint is set after the call to drawCrosshairLines the first time around rather than before, and is one pan point behind each subsequent time - hence in my example above I set crosshairPoint before the super call.

Thanks for your help.

Cheers,

Marcus


#14

Hi Marcus,

Thanks for pointing that out! I’ve edited the post. I’d originally written the code in Objective-C and translated into Swift quickly for this post, seems I made a mistake whilst doing that.

Cheers,

Matt