Search code examples
swiftmacosios-charts

Programatically Zoom into section of chart where a marker is being displayed


In a Mac app I am trying to zoom into a section on x axis where a Marker is currently visible.

I am using some logic as per below to figure out the x position:

private func getZoomXPosition() -> CGFloat {
    var x = 0.0
    if let highlight = self.chartView.highlighted.first {
        print("highlight x=\(highlight.x) drawX=\(highlight.drawX)")
        let pos = chartView.getMarkerPosition(highlight: highlight)
        x = pos.x
    }
    print("zoom to x: \(x)")
    return x
}

If a highlight exists (then we have a marker showing at that position)

I am then calling:

self.chartView.zoom(scaleX: 1.5, scaleY: 1.0, x: x, y: 0)

To perform the zoom to that x position. This appears to work mostly except once you get to a certain zoom level the next zoom in appears to zoom to the wrong point.

clicked ChartDataEntry, x: 3125.0, y 393.0
highlight x=3125.0 drawX=286.3808824269045
zoom to x: 286.3808824269045
clicked ChartDataEntry, x: 3125.0, y 393.0
highlight x=3125.0 drawX=272.58693711440446
zoom to x: 272.58693711440446
clicked ChartDataEntry, x: 3125.0, y 393.0
highlight x=3125.0 drawX=262.2414781300295
zoom to x: 262.2414781300295
clicked ChartDataEntry, x: 3125.0, y 393.0
highlight x=3125.0 drawX=258.36193101088895
zoom to x: 258.36193101088895
clicked ChartDataEntry, x: 3125.0, y 393.0
highlight x=3125.0 drawX=259.0893460957275
zoom to x: 259.0893460957275
clicked ChartDataEntry, x: 3125.0, y 393.0
highlight x=3125.0 drawX=258.3391992894881
zoom to x: 258.3391992894881
clicked ChartDataEntry, x: 3125.0, y 393.0
highlight x=3125.0 drawX=260.06219273506895
zoom to x: 260.06219273506895
clicked ChartDataEntry, x: 3125.0, y 393.0
highlight x=3125.0 drawX=252.83369679540647
zoom to x: 252.83369679540647
clicked ChartDataEntry, x: 3125.0, y 393.0
highlight x=3125.0 drawX=303.74376780794955
zoom to x: 303.74376780794955

as you can see by the logs the 9th zoom in always causes the issue.

Update 18 march:

I've updated method above to include the prints.

So I'm finding the current highlighted position on the chart

chartView.hightlighted.first

then I am converting this to the actual point on the chart

chartView.getMarkerPosition(highlight: highlight

and then finally calling

cahrtView.zoom()

with that x and an x scale factor of 1.5.

It works fine up until the 8th level zooming in (by clicking a zoom in button). the 9th one always zooms into the wrong part of the chart.


Solution

  • There are a bunch of zoom functions in ChartViewBase, and I've had trouble wrapping my head around all of them, so I've only been using one particular one and writing helper functions to call it the way I prefer. Hopefully this will work correctly for you:

    func zoomToPosition(
        _ position: CGPoint,
        xMultiplier: Double,
        yMultiplier: Double,
        axis: YAxis.AxisDependency
    ) {
        // position is a pixel location.
        // valuePosition is the corresponding value in the chart for the pixel
        let valuePosition = chartView.valueForTouchPoint(point: position, axis: axis)
        
        // the version of `zoom` that we'll call seems to work best with
        // x and y scales calculated manually rather than using the built-in
        let newXScale = chartView.viewPortHandler.scaleX * xMultiplier
        let newYScale = chartView.viewPortHandler.scaleY * yMultiplier
        
        chartView.zoom(scaleX: newXScale, scaleY: newYScale, xValue: valuePosition.x, yValue: valuePosition.y, axis: axis)
    }
    

    I just tried using this code to zoom in on my existing project, and it works well beyond ten iterations.

    Here's how I can see it being used in your case:

    let xPos = getZoomXPosition()
    let zoomPos = CGPoint(x: xPos, y: 0.0)
    zoomToPosition(zoomPos, xMultiplier: 1.4, yMultiplier: 1.0, axis: .left)
    

    Fingers crossed that this will work :-)