I am using excellent iOS Chart library for creating graphs. (https://github.com/danielgindi/Charts/)
Here is what I am be able to do:
I would to have labels like this:
Do you know how to do this?
Thanks
You can shift the index by 0.5 and set xAxis.axisMinimum to 0.0:
var entries: [BarChartDataEntry] = []
for index in 0..<datapoints.count {
let value1 = Double(datapoints[index])
let value2 = Double(datapoints[index])
entries.append(BarChartDataEntry(x: Double(index)+0.5, yValues: [value1, value2]))
}
let xvalues = ["1", "2"]
combinedChart.xAxis.valueFormatter = IndexAxisValueFormatter(values: xvalues)
combinedChart.xAxis.labelPosition = Charts.XAxis.LabelPosition.bothSided
let set = BarChartDataSet(values: entries, label: " - Description.")
combinedChart.xAxis.axisMinimum = 0.0
set.axisDependency = Charts.YAxis.AxisDependency.left
data.addDataSet(set)
Result:
There are 2 ways to complete this job.
First method
chartView.setExtraOffsets(left: 20, top: 0, right: 0, bottom: 0)
Second method
chartView.xAxis.avoidFirstLastClippingEnabled = true
You could also use offset
combinedChart.xAxis.xOffset = -0.5
I am not sure if the value is correct for your case. Try it out a little bit.
Related
The task is to add 10 buttons (0...9) with labels using for in loop.
I created buttons based on class ButtonPrototype. I assigned label to each button via counter inside for in loop.
It works, but there is incorrect labels order:
I need another order:
How can I implement correct order?
Code:
func createButtons() {
for y in 0...1 {
for x in 0...4 {
counterForLoop += 1
self.button = ButtonPrototype(pos: .init( CGFloat(x)/7, CGFloat(y)/7, 0 ), imageName: "\(counterForLoop)")
parentNode.addChildNode(button)
parentNode.position = SCNVector3(x: 100,
y: 100,
z: 100)
}
}
}
The following approach perfectly makes the trick:
for y in 0...1 {
for x in 0...4 {
let textNode = SCNNode()
let ascendingOrder: String = "\(((x+1)+(y*5)) % 10)"
let geo = SCNText(string: ascendingOrder, extrusionDepth: 0.5)
geo.flatness = 0.04
geo.firstMaterial?.diffuse.contents = UIImage(named: ascendingOrder)
textNode.geometry = geo
textNode.position = SCNVector3(x*10, -y*10, 0)
sceneView.scene?.rootNode.addChildNode(textNode)
print(ascendingOrder)
}
}
You have at least two problems with your code. Your smallest button label is in the lower left and you want it to be in the lower right, and your labels go 0-9, and you want them to go from 1 to 10 (but display 10 as “0”).
To reverse the x ordering, change X to 10-x in your creation of a position, and change your imageName to “((counterForLoop+1)%10)”:
self.button = ButtonPrototype(
pos: .init(
CGFloat(10-x)/7,
CGFloat(y)/7,
0),
imageName: "\((counterForLoop+1)%10)")
By the way, you should add a SceneKit tag to your question. That seems more important than either the label tag or the loops tag.
I'm struggling with charts (I use the wonderful Charts library). I had another problem, the solution of which I could not find.
I have a horizontal chart, everything is relatively fine with it, but I need the new bar that I add to be of a different color and with a new label on the y-axis (in a horizontal chart, this is the x-axis).
This is what my chart looks like
This is what I need to get
This is the code where you can see how I add the bar
let barWidth = 5.3
let spaceForBar = 10.3
var n = 0
var yVals = (0..<count).map { (i) -> BarChartDataEntry in
let val = items.map{Double($0.iqLevel)}[n]
n += 1
return BarChartDataEntry(x: Double(i)*spaceForBar, y: val)
}
let userData = testResultItem(group: "New user", iqLevel: 118).iqLevel
let newEntry = BarChartDataEntry(x: 2.5*spaceForBar, y: Double(userData))
yVals.append(newEntry)
let chartDataSet = BarChartDataSet(entries: yVals)
I would be immensely grateful to everyone who can help at least some advice!
You can create a separate BarChartDataSet for this single user entry and set a different color for it.
let yVals = (0..<count).map { (i) -> BarChartDataEntry in
let val = items.map{Double($0.iqLevel)}[n]
n += 1
return BarChartDataEntry(x: Double(i)*spaceForBar, y: val)
}
let userData = testResultItem(group: "New user", iqLevel: 118).iqLevel
let userEntry = BarChartDataEntry(x: 2.5*spaceForBar, y: Double(userData))
let chartDataSet = BarChartDataSet(entries: yVals)
chartDataSet.setColor(YourCurrentColor)
let userDataSet = BarChartDataSet(entries: [userEntry])
userDataSet.setColor(DarkGreenColor) //For user entry
let barChartData = BarChartData(dataSets: [chartDataSet, userDataSet])
I am using Charts to create my own charts but I am struggling to see the values of my table with decimals instead of just integers.
My chart init is like below, but I cannot find the way to format the points to show the decimal parts since they are doubles. I have tried to set targetChartView.xAxis.decimals and targetChartView.leftAxis.decimals without result.
How can I enable the decimal notation?
init(withGraphView aGraphView: LineChartView, noDataText aNoDataTextString: String, minValue aMinValue: Double, maxValue aMaxValue: Double, numberOfDataSets aDataSetCount: Int, dataSetNames aDataSetNameList: [String], dataSetColors aColorSet: [UIColor], andMaxVisibleEntries maxEntries: Int = 10) {
originalMaxValue = aMaxValue
originalMinValue = aMinValue
dateFormatter = DateFormatter()
targetChartView = aGraphView
lineChartData = LineChartData()
maximumVisiblePoints = maxEntries
timestamps = [Date]()
for i in 0..<aDataSetCount {
let firstEntry = ChartDataEntry(x: 0, y: 0)
var entries = [ChartDataEntry]()
entries.append(firstEntry)
let aDataSet = LineChartDataSet(values: entries, label: aDataSetNameList[i])
aDataSet.setColor(aColorSet[i])
aDataSet.lineWidth = 3
aDataSet.lineCapType = .round
aDataSet.drawCircleHoleEnabled = false
aDataSet.circleRadius = 2
aDataSet.axisDependency = .left
aDataSet.highlightEnabled = true
lineChartData.addDataSet(aDataSet)
}
targetChartView.data = lineChartData
targetChartView.noDataText = aNoDataTextString
targetChartView.chartDescription?.text = ""
targetChartView.rightAxis.drawLabelsEnabled = false
targetChartView.xAxis.drawGridLinesEnabled = false
targetChartView.xAxis.labelPosition = .bottom
targetChartView.leftAxis.drawGridLinesEnabled = false
targetChartView.dragEnabled = true
targetChartView.xAxis.granularityEnabled = true
targetChartView.xAxis.granularity = 1
targetChartView.xAxis.decimals = 0
targetChartView.leftAxis.granularityEnabled = true
targetChartView.leftAxis.granularity = 1
targetChartView.leftAxis.decimals = 0
targetChartView.xAxis.axisMinimum = Double(0)
targetChartView.xAxis.axisMaximum = Double(maximumVisiblePoints)
targetChartView.leftAxis.axisMinimum = aMinValue
targetChartView.leftAxis.axisMaximum = aMaxValue
targetChartView.setScaleEnabled(false)
super.init()
targetChartView.xAxis.valueFormatter = self
targetChartView.delegate = self
// This gesture recognizer will track begin and end of touch/swipe.
// When user presses the graph we don't want it to be moving when new data is received even when the most recent value is visible.
let clickRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress))
clickRecognizer.minimumPressDuration = 0
clickRecognizer.delegate = self
targetChartView.addGestureRecognizer(clickRecognizer)
}
The decimals property is used only if you are using the default formatter. However, I see that you are also setting
targetChartView.xAxis.valueFormatter = self
That means your class is implementing IAxisValueFormatter and its stringForValue(:axis:) method. The value in decimals (which should be nonzero) is then ignored because you have a custom formatter.
You can either remove the assignment and then your decimals should be displayed or, you will have to format your decimals correctly in stringForValue(:axis:).
You have not added this part of your implementation but the problem is probably there.
I see there is also some magic in the AxisRenderer that will probably remove decimals if the interval between values is bigger than 1. Therefore using a custom formatter for both axes is probably the best solution.
As I mentioned to #Sulthan, the issue was not in the chart itself but how the data set (aDataSet) had to be formatted, so adding the snippet below enabled three decimals on my data in the graph
init(withGraphView aGraphView: LineChartView, noDataText aNoDataTextString: String, minValue aMinValue: Double, maxValue aMaxValue: Double, numberOfDataSets aDataSetCount: Int, dataSetNames aDataSetNameList: [String], dataSetColors aColorSet: [UIColor], andMaxVisibleEntries maxEntries: Int = 10) {
originalMaxValue = aMaxValue
originalMinValue = aMinValue
dateFormatter = DateFormatter()
targetChartView = aGraphView
lineChartData = LineChartData()
maximumVisiblePoints = maxEntries
timestamps = [Date]()
//Setting 3 decimals for the number that are going to be displayed
let formatter = NumberFormatter()//ADD THIS
formatter.numberStyle = .decimal//ADD THIS
formatter.maximumFractionDigits = 3//ADD THIS
formatter.minimumFractionDigits = 3//ADD THIS
for i in 0..<aDataSetCount {
let firstEntry = ChartDataEntry(x: 0, y: 0)
var entries = [ChartDataEntry]()
entries.append(firstEntry)
let aDataSet = LineChartDataSet(values: entries, label: aDataSetNameList[i])
aDataSet.setColor(aColorSet[i])
aDataSet.lineWidth = 3
aDataSet.lineCapType = .round
aDataSet.drawCircleHoleEnabled = false
aDataSet.circleRadius = 2
aDataSet.axisDependency = .left
aDataSet.highlightEnabled = true
lineChartData.addDataSet(aDataSet)
//Use formater that allows showing decimals
lineChartData.setValueFormatter(DefaultValueFormatter(formatter: formatter))//ADD THIS
}
...}
When I have multiple points in an array for a line on a line graph, everything shows perfectly.
But when there is only one point, the dot does not show. I dont know why?
the delegate is being set elsewhere, but this doesnt seem to be the issue.
The below examples shows Test 2 and Test exercise. The first image is where each has one value, the second they each have 2.
heres my code
func startChart(){
chart.dragEnabled = true
chart.legend.form = .circle
chart.drawGridBackgroundEnabled = false
let xaxis = chart.xAxis
xaxis.valueFormatter = axisFormatDelegate
xaxis.labelCount = dataSets.count
xaxis.labelPosition = .bottom
xaxis.granularityEnabled = true
xaxis.granularity = 1.0
xaxis.avoidFirstLastClippingEnabled = true
xaxis.forceLabelsEnabled = true
let rightAxis = chart.rightAxis
rightAxis.enabled = false
rightAxis.axisMinimum = 0
let leftAxis = chart.leftAxis
leftAxis.drawGridLinesEnabled = true
leftAxis.axisMinimum = 0
let chartData = LineChartData(dataSets: dataSets)
chart.data = chartData
}
If I add
chart.setVisibleXRangeMinimum(myMinDate)
the value will show correctly. however it squashes the value to the left and overlaps 2 x value dates
The only way I could get around this was to add an additional invisible line.
I created a clear line that started the day before and ended the day after my single values.
As long as there is a line on the chart that goes from one point to another, the other single values show.
var singleValue = false
for i in 0...(dataSets.count - 1) {
if dataSets[i].values.count > 1{
singleValue = true
}
}
var data = dataSets
if singleValue == false {
let minNS = Calendar.current.date(byAdding: .day, value: -1, to: minNSDate as! Date)
let maxNS = Calendar.current.date(byAdding: .day, value: 1, to: maxNSDate as! Date)
var dataEntries: [ChartDataEntry] = []
let dataEntry1 = ChartDataEntry(x:Double(String(format: "%.2f",Double((minNS?.timeIntervalSince1970)!)))!,y:00.00)
let dataEntry2 = ChartDataEntry(x:Double(String(format: "%.2f",Double((maxNS?.timeIntervalSince1970)!)))!,y:00.00)
dataEntries.append(dataEntry1)
dataEntries.append(dataEntry2)
let set = LineChartDataSet(values: dataEntries, label: "")
set.setCircleColor(UIColor.clear)
set.circleHoleColor = UIColor.clear
set.setColor(UIColor.white, alpha: 0.0)
set.drawValuesEnabled = false
data.append(set)
}
chart.chartDescription?.text = ""
let chartData = LineChartData(dataSets: data)
chart.data = chartData
I think I found a better solution. Single point is not enough to draw a line (you need at least two points) so LineChartView can't render your data. You can fix that by replace LineChartView with CombinedChartView. CombinedChartView give a possibility to mix different types of data on one chart. You can check how many data entires do you have and decide which type of DataSet will be proper.
Code example:
if dataEntry.count == 1 {
let scatterDataSet = ScatterChartDataSet(values: dataEntry, label: title)
scatterDataSet.setColor(UIColor.pmd_darkBlue)
scatterDataSet.setScatterShape(.circle)
scatterDataSet.drawValuesEnabled = false
combinedChartData.scatterData = ScatterChartData(dataSets: [scatterDataSet])
}
else {
let lineDataSet = LineChartDataSet(values: dataEntry, label: title)
lineDataSet.setColor(UIColor.pmd_darkBlue)
lineDataSet.lineWidth = 3.0
lineDataSet.drawCirclesEnabled = false
lineDataSet.drawValuesEnabled = false
combinedChartData.lineData = LineChartData(dataSets: [lineDataSet])
}
combinedChart.data = combinedChartData
You can also combine two and more types DataSets in one chart.
Important
Don't forget to add this line:
combinedChart.drawOrder = [DrawOrder.line.rawValue, DrawOrder.scatter.rawValue]
You must to write types of data types you use otherwise data will not render.
I’m using iOS Charts.framework (https://github.com/danielgindi/Charts) in my swift 3.0 xcodeproject.
I know that "All dataset constructors have changed - they do not take an array of x-indices anymore" but how do I put strings on my x axis now if LineChartData doesn't have a "xVals" parameters anymore???
Before was so easy to do that...
let weights: [Double] = self.getWeigths()
let weightDates: [String] = self.getWeightDates()
var yValues : [ChartDataEntry] = [ChartDataEntry]()
for i in 0 ..< dateLastWeights.count {
let entry = ChartDataEntry(x: Double(i), y: weights[i])
yValues.append(entry)
}
let set: LineChartDataSet = LineChartDataSet(values: yValues, label: "First Set")
var dataSets : [LineChartDataSet] = [LineChartDataSet]()
dataSets.append(set)
let data: LineChartData = LineChartData(xVals: weightDates, dataSets: dataSets)
Since 3.0.1 version, you can also do:
lineChart.xAxis.valueFormatter = IndexAxisValueFormatter(values: labels)
lineChart.xAxis.granularity = 1.0
After trying lots of things and discussing with others developers we made it!
self.lineChart.xAxis.granularity = 1
self.lineChart.xAxis.valueFormatter = DefaultAxisValueFormatter(block: { (index, _) -> String in
return self.dateLastWeights[Int(index)]
})