Reputation: 1935
I have a lineChart and I want to add a single point (scattered point).
Is that possible?
Is there any workaround?
I tried to figure it out by to me seems impossible since even using scatter data only I have to use the same xVals
array, and in my case the line has some xVals
points and the single scattered point only one value on X and one on Y.
Thanks
EDIT:
This is what I want to achieve:
To me the only solution using iOS-charts is to superimpose two view charts...
Upvotes: 0
Views: 2767
Reputation: 9754
Are you asking for truncating the line chart? I am not clear what's your question. If you just want scatter chart + line chart, use CombinedChartView
.
I had a feature request for truncating line chart, so we can have 'single point' in the line chart, so if you don't want to connect the dots. Check if it is https://github.com/danielgindi/ios-charts/issues/280. If true, you could help comment so the author will know it.
On your side, what you can try is to subclass the line chart renderer and protocol to allow you add single dots. For me, I want to truncate the line if the current dot cannot be connected to the next one. such as y values: 1-2-3 5(single dot) 7-8-9.
You have to provide what you want so I can help you better.
For your ask in comment, I can only provide a few code get you started, but you have to fully understand what am I trying to do.
Knowledge you must have: CoreGraphics + CGAffineTransform (iOS), viewPortHandler, transformer, axisRenderer (ios-charts).
In my problem, I will subclass ScatterChartView, so I can draw xAxis like yAxis. The idea is to disable xAxis, and let rightAxis to be HorizontalBarChartYAxisRenderer acting as xAxis.
First, subclass ScatterChartView and override initialize():
public override func initialize() {
super.initialize();
doubleTapToZoomEnabled = false;
xAxis.enabled = false;
// to determine how much space left for the dots to be drawn
_leftYAxisRenderer = ChartYAxisRenderer(viewPortHandler: _viewPortHandler, yAxis: _leftAxis, transformer: _leftAxisTransformer);
// right axis will be the bottom axis above xAxis
_rightAxisTransformer = ChartTransformerHorizontalBarChart(viewPortHandler: _viewPortHandler);
_rightYAxisRenderer = MyChartYAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, yAxis: _rightAxis, transformer: _rightAxisTransformer);
renderer = MyScatterChartRenderer(delegate: self, animator: _animator, viewPortHandler: _viewPortHandler);
}
override prepareValuePxMatrix
internal override func prepareValuePxMatrix()
{
// replace the chartXMin and deltaX with rightAxis values to keep the CTM correct
_rightAxisTransformer.prepareMatrixValuePx(chartXMin: _rightAxis.axisMinimum, deltaX: CGFloat(_rightAxis.axisRange), deltaY: CGFloat(_leftAxis.axisRange), chartYMin: _leftAxis.axisMinimum);
_leftAxisTransformer.prepareMatrixValuePx(chartXMin: _rightAxis.axisMinimum, deltaX: CGFloat(_rightAxis.axisRange), deltaY: CGFloat(_leftAxis.axisRange), chartYMin: _leftAxis.axisMinimum);
}
Until now, you have create the rightAxis acting as xAxis, and generate its matrix.
Now for the data part, I will create two dataSets, and assign them to AxisDependencyLeft and AxisDependencyRight via dataSet.axisDependency, so I can know how to render them.
In the chart renderer, I will read the two dataSets, one for x and one for y, and utilize the transformer to get the postion to draw.
such as:
var xDataSet = scatterData.getRight()
var yDataSet = scatterData.getLeft()
var dataSet = yDataSet as! ScatterChartDataSet
var trans = delegate!.scatterChartRenderer(self, transformerForAxis: dataSet.axisDependency)
var phaseX = _animator.phaseX
var phaseY = _animator.phaseY
var xEntries = (xDataSet as! ScatterChartDataSet).yVals
var entries = dataSet.yVals
// if count not equal, something goes off
if (xEntries.count != entries.count)
{
return
}
var shapeSize = dataSet.scatterShapeSize
var shapeHalf = shapeSize / 2.0
var point = CGPoint()
var valueToPixelMatrix = trans.valueToPixelMatrix
var shape = dataSet.scatterShape
CGContextSaveGState(context)
for (var j = 0, count = Int(min(ceil(CGFloat(entries.count) * _animator.phaseX), CGFloat(entries.count))); j < count; j++)
{
var e = entries[j]
var xEntry = xEntries[j]
point.x = CGFloat(xEntry.value)
point.y = CGFloat(e.value) * phaseY
point = CGPointApplyAffineTransform(point, valueToPixelMatrix)
// now you have the point to render, such as:
if (shape == .Circle)
{
CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor)
var rect = CGRect()
rect.origin.x = point.x - shapeHalf
rect.origin.y = point.y - shapeHalf
rect.size.width = shapeSize
rect.size.height = shapeSize
CGContextFillEllipseInRect(context, rect)
}
}
I cannot provide all the code I have. You have to understand what am I doing, so you can imagine yours. The only magic is you get a chance to define how you want to render it.
Upvotes: 1