Convert decimal to integer on line chart - ios

I am using this lib chart : https://github.com/danielgindi/Charts
I have one line chart. Which will display values on x axis. But now it's showing in decimal like 22.0. But i want like 22
Here is my code :
func updateGraphMaster(xAxisLabel: String) {
var lineChartEntry = [ChartDataEntry]()
for num in 0..<chartDataDoubles.count {
let value = ChartDataEntry(x: Double(num), y: chartDataDoubles[num])
lineChartEntry.append(value)
}
let lineChartDataSet = LineChartDataSet(entries: lineChartEntry, label: xAxisLabel)
lineChartDataSet.colors = [NSUIColor.white]
lineChartDataSet.highlightEnabled = true
lineChartDataSet.highlightColor = UIColor.white
lineChartDataSet.drawVerticalHighlightIndicatorEnabled = false
lineChartDataSet.drawHorizontalHighlightIndicatorEnabled = false
lineChartDataSet.drawValuesEnabled = true
lineChartDataSet.drawCirclesEnabled = false
lineChartDataSet.drawCircleHoleEnabled = false
let gradient = getGradientFilling()
lineChartDataSet.fill = Fill.fillWithLinearGradient(gradient, angle: 90.0)
lineChartDataSet.drawFilledEnabled = true
let data = LineChartData()
data.addDataSet(lineChartDataSet)
data.setDrawValues(false)
data.setValueTextColor(NSUIColor.white)
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 0
formatter.numberStyle = .none
formatter.locale = .current
//lineChartDataSet.valueFormatter = DefaultValueFormatter(formatter: formatter)
data.setValueFormatter(DefaultValueFormatter(formatter: formatter))
chartView.data = data
chartView.xAxis.valueFormatter = DefaultAxisValueFormatter(formatter: formatter)
}
Here i using setValueFormatter and append the data to chart view. But still it's showing in decimal . Not in integer.

if you want to edit the data set value format itself not the axis, try this:
data.valueFormatter = DefaultValueFormatter(decimals: 0)
I hope It works!
It seems that you have more than 1 data set to show on chart?

Your question was unclear to me until you showed us a screenshot
You were not asking for the actual drawn values to be formatted, you were asking to format the value displayed by the label of the marker that appears when you click a point. For that implementation, I had to presume you used the custom class BalloonMarker from the Charts-Demo, which does not format the value of its text before being displayed. I was able to reproduce your issue and fix it: note the lines 184-187 of BalloonMarker.swift:
184 open override func refreshContent(entry: ChartDataEntry, highlight: Highlight)
185 {
186 setLabel(String(entry.y))
187 }
Line 186 needs to be replaced with
186 setLabel(String(format: "%.0f", entry.y))
That should fix your issue. I include my reproduction and fix for you below:
Reproduction of your issue
With Fix
Additionally, if you wish to ensure your x-axis shows only integers you need to do the following:
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 0
formatter.numberStyle = .none
formatter.locale = .current
chartView.xAxis.valueFormatter = DefaultAxisValueFormatter(formatter: formatter)

You want to set formatter.maximumFractionDigits to zero.

Related

x-Axis value alignment with y-Axis value is wrong Charts-iOS

I have an issue plotting the x-axis against its corresponding values there is no major customisation done except the values are being plotted on the right axis and axis dependency is set to right
Below is the screenshot
Setup code:
let xAxis = evalutionTrendView?.trendGraphView.xAxis
xAxis?.labelPosition = .bottom
xAxis?.drawGridLinesEnabled = false
xAxis?.drawAxisLineEnabled = true
//xAxis?.axisLineColor = Utilities.axisBaseLineColor
xAxis?.forceLabelsEnabled = true
xAxis?.avoidFirstLastClippingEnabled = false
xAxis?.labelFont = UIFont.SFProText.Regular(12.0)
xAxis?.labelTextColor = Utilities.axisValueColor
xAxis?.granularity = 1
xAxis?.granularityEnabled = true
xAxis?.valueFormatter = XaxisValueFormatter()
let rightAxis = evalutionTrendView?.trendGraphView.rightAxis
rightAxis?.gridColor = Utilities.axisBaseLineColor
rightAxis?.gridLineWidth = 1.0
rightAxis?.axisMaximum = 100
rightAxis?.axisMinimum = 0
rightAxis?.axisLineColor = .clear
rightAxis?.labelFont = UIFont.SFProText.Regular(12.0)
rightAxis?.labelTextColor = Utilities.axisValueColor
evalutionTrendView?.trendGraphView?.leftAxis.enabled = false
final class XaxisValueFormatter: AxisValueFormatter{
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let date = Date(timeIntervalSince1970: TimeInterval(value))
print("x-axis: \(date.dayMonthSlash), value:\(value)")
return date.dayMonthSlash
}
}
Library code:
https://github.com/danielgindi/Charts
Anyone has faced similar issue using with above library with respect to x-axis alignment ?

Currency NumberFormatter - omit fraction part when the number is integer

I need a specific behaviour from my NumberFormatter used to output currency:
If the number is integer (0, 0.00) it should not show decimal separator (0 €)
Else (123.90, 12.1), it should show two digits after decimal separator (123.90 €, 12.10 €).
The way I create and use my formatter now is the following:
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencySymbol = "€"
formatter.alwaysShowsDecimalSeparator = false
let num = formatter.string(from: 123.9)!
If created like this, the formatter always shows decimal separator, despite the fact that I set this property to false.
How can I achieve this?
Currency numberStyle is always returning decimal point followed two digits. So if you want to achieve your goal, you should modify the output string by yourself.
Please check the example code:
let number1: Double = 123.908392857
let number2: Int = 123
let number3: Float = 123.00
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencySymbol = "€"
let num1 = formatter.string(from: NSNumber(value: number1))! // Output €123.91
let num2 = formatter.string(from: NSNumber(value: number2))! // Output €123.00
let num3 = formatter.string(from: NSNumber(value: number3))! // Output €123.00
print("\(num1) \(num2) \(num3)")
// This is trick
let newNum1 = trimString(string: num1) // Output €123.91
let newNum2 = trimString(string: num2) // Output €123
let newNum3 = trimString(string: num3) // Output €123
print("\(newNum1) \(newNum2) \(newNum3)")
trimString is a simple trim function, you can put it in the String extension or any place you want.
func trimString(string: String) -> String {
if string.hasSuffix(".00") {
return String(string.dropLast(3))
}
else {
return string
}
}
You may have question about why alwaysShowsDecimalSeparator is not working then? It is for .decimal numberStyle and default decimalSeparator is "."

Candlestick chart not appearing correctly in ios using danielgindi/Charts framework

var dataEntries: [ChartDataEntry] = []
let financialData = data
for chartData in financialData{
let dataEntry = CandleChartDataEntry(x: chartData.date, shadowH: chartData.High, shadowL: chartData.Low, open: chartData.Open, close: chartData.Close)
dataEntries.append(dataEntry)
}
let chartDataSet = CandleChartDataSet(values: dataEntries, label: "")
chartDataSet.axisDependency = .left
chartDataSet.drawValuesEnabled = false
chartDataSet.increasingColor = UIColor.green
chartDataSet.increasingFilled = false
chartDataSet.decreasingColor = UIColor.red
chartDataSet.decreasingFilled = true
chartDataSet.barSpace = 0.25
chartDataSet.shadowColor = UIColor.darkGray
chartDataSet.neutralColor = UIColor.blue
let chartData = CandleChartData(dataSet: chartDataSet)
chartView.data = chartData
let xaxis = chartView.xAxis
xaxis.valueFormatter = axisFormatDelegate
Below is the sample data set that is being received from server and the image showing the chart being displayed.
https://api.cryptowat.ch/markets/gdax/btcusd/ohlc?periods=60
[ CloseTime, OpenPrice, HighPrice, LowPrice, ClosePrice, Volume ]
I need help in fixing the display of data as currently it doesn't resemble a candle stick chart in any way. Can someone please help me in what I am doing wrong here.
CandleStick chart data being displayed in the app
So just in case anyone is having the same problem, i was having issues with the candles too and i found a solution based on this post:
https://github.com/danielgindi/Charts/issues/2742
Create a class for your chartView.xAxis.valueFormatter that supports your entries with your init.
Once you have your results entries, i used the enumerated value for the x on the CandleChartDataEntry
In the stringForValue method inside your class that inherits from IAxisValueFormatter work the way you want to present your string, in my case was dates.
First two points:
//Check nil results
if result != nil {
//Save to Holder for future use
self.historyData = result
// Set the formatter class for the chartView
self.chartView.xAxis.valueFormatter = dateFormatter(elementsForFormatter: self.historyData!)
//Create a new chart data entry
var chartEntries = [ChartDataEntry]()
//Loop results
result?.enumerated().forEach({ (i, element) in
// I'm force unwrapping, but please take care of this don't do me...
let newDataEntryValue = CandleChartDataEntry(x:Double(i),
shadowH: element["high"] as! Double,
shadowL: element["low"] as! Double,
open: element["open"] as! Double,
close: element["close"] as! Double)
chartEntries.append(newDataEntryValue)
})
}
// Do rest of conf. for your chart....
Last point (class implementation):
class dateFormatter: IAxisValueFormatter {
var elements:Array<Dictionary<String,Any>>
init(elementsForFormatter:Array<Dictionary<String,Any>>) {
self.elements = elementsForFormatter
}
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .short
//Get the date based on the value which is the index
if let time = self.elements[Int(value)]["time"] as? Double {
print(time)
let dateString = formatter.string(from: Date(timeIntervalSince1970: TimeInterval(time)))
return dateString
} else {
return ""
}
}
}
Hope this helps someone!

Force to show min/max on x axis

I am working with library. Anybody know if is it possible to force show min and max label on x-axis.
What I have:
What I want to achieve:
My implementation:
private func prepareXAxis() {
lineChart.xAxis.labelPosition = .bottom
lineChart.xAxis.valueFormatter = ChartXAxisDateValueFormatter()
lineChart.xAxis.labelFont = UIFont.pmd_robotoRegular(ofSize: 13.0)
lineChart.xAxis.labelTextColor = UIColor.pmd_darkGrey
}
And implementation of my own ValueFormatter:
import Foundation
import Charts
class ChartXAxisDateValueFormatter: NSObject, IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "hh:mm\ndd MMM"
return dateFormatter.string(from: Date(timeIntervalSince1970: value))
}
}
Edit
I added this code but first and last label on x-axis is still skipped:
lineChart.xAxis.axisMinimum = lineDataSet.xMin
lineChart.xAxis.axisMaximum = lineDataSet.xMax
lineChart.xAxis.avoidFirstLastClippingEnabled = false
lineChart.xAxis.granularity = 1.0
I checked lineDataSet.xMin and lineDataSet.xMax. They have valid values.
You need to set below property for not skip 1st and last value
//Set up XAxis into chart.
lineChart.xAxis.axisMinimum = 0
lineChart.xAxis.avoidFirstLastClippingEnabled = NO
lineChart.xAxis.granularity = 1.0
Hope this will helps.
I found a solution. The lost line:
combinedChart.xAxis.forceLabelsEnabled = true
I also changed type of chart from LineChart to CombinedChartView.

Choosing units with MeasurementFormatter

This is similar to a question I asked yesterday but the answer I got doesn't seem to work in this case.
I'm getting altitude values in meters from Core Location. I want to display these in a localized form. As an example, the altitude where I am right now is 1839m above sea level. This should be displayed as 6033 feet. The best I can do with MeasurementFormatter is "1.143 mi".
let meters : Double = 1839
let metersMeasurement = Measurement(value: meters, unit: UnitLength.meters)
let measurementFormatter = MeasurementFormatter()
measurementFormatter.locale = Locale(identifier: "en_US")
let localizedString = measurementFormatter.string(from: metersMeasurement)
The .naturalScale option that answered my previous question doesn't help here. I think this is a limitation of the framework, but I wonder if anyone has a workaround for now.
You just need to convert your UnitLength from meters to feet. You can also create a custom US measurement formatter to display it as needed:
extension Measurement where UnitType == UnitLength {
private static let usFormatted: MeasurementFormatter = {
let formatter = MeasurementFormatter()
formatter.locale = Locale(identifier: "en_US")
formatter.unitOptions = .providedUnit
formatter.numberFormatter.maximumFractionDigits = 0
formatter.unitStyle = .long
return formatter
}()
var usFormatted: String { Measurement.usFormatted.string(from: self) }
}
Playground
let value: Double = 1839
let meters: Measurement<UnitLength> = .init(value: value, unit: .meters)
let feet = meters.converted(to: .feet)
let formatted = feet.usFormatted
print(formatted) // "6,033 feet"\n
I think you are correct there's no way to specify this kind of context. You could do something like:
extension MeasurementFormatter
{
func altitudeString(from measurement: Measurement<UnitLength>) -> String
{
var measurement = measurement
let unitOptions = self.unitOptions
let unitStyle = self.unitStyle
self.unitOptions = .naturalScale
self.unitStyle = .long
var string = self.string(from: measurement)
if string.contains(self.string(from: UnitLength.miles))
{
self.unitStyle = unitStyle
measurement.convert(to: UnitLength.feet)
self.unitOptions = .providedUnit
string = self.string(from: measurement)
}
else if string.contains(self.string(from: UnitLength.kilometers))
{
self.unitStyle = unitStyle
measurement.convert(to: UnitLength.meters)
self.unitOptions = .providedUnit
string = self.string(from: measurement)
}
else
{
self.unitStyle = unitStyle
string = self.string(from: measurement)
}
self.unitOptions = unitOptions
return string
}
}
Maybe there are other culturally specific ways of measuring elevation, but this would seem better than miles and kilometers.

Resources