28

With the new release i had some troubles to create some graphs the previous code was:

func setChart(dataPoints: [String], values: [Double]) {
    var dataEntries: [BarChartDataEntry] = []

    for i in 0..<dataPoints.count {
        let dataEntry = BarChartDataEntry(value: values[i], xIndex: i)
        dataEntries.append(dataEntry)
    }

    let chartDataSet = BarChartDataSet(yVals: dataEntries, label: "Units Sold")
    let chartData = BarChartData(xVals: months, dataSet: chartDataSet)
    barChartView.data = chartData
}

You can pass the values for example an array of months using the line:

let chartData = BarChartData(xVals: months, dataSet: chartDataSet)

After the new release the code to implement the same graph is:

func setChart(dataPoints: [String], values: [Double]) {          
    var dataEntries: [BarChartDataEntry] = []

    for i in 0..<dataPoints.count {
        let dataEntry = BarChartDataEntry(x: Double(i+2), y:values[i], data: months )
        dataEntries.append(dataEntry)
    }

    let chartDataSet = BarChartDataSet(values: dataEntries, label: "Units Sold")

    let chartData = BarChartData()
    chartData.addDataSet(chartDataSet)
    barChartView.data = chartData
}

I was trying a few hours but i couldn't find a way to modify the X axis values, i hope someone can help me, thanks!!

Tobi
  • 137
  • 2
  • 12
  • I think you can use axis value formatter. barChartView.xAixs.valueFormatter = "Your value formatter object" – Surely Aug 20 '16 at 03:50
  • 1
    :O, thank you rmaddy, i'm trying to use the "barChartView.xAxis.valueFormatter" but it needs an "iAxisValueFormatter?", wich is an interface, I'm a beginner and don't know how to use this type, could you please give me some instructions about how to use it? – Julio Cesar Aguilar Jiménez Aug 21 '16 at 04:17
  • hi, you can just create a class that implement the interface, then create an object of the class, or you can just implement the interface with your current class and pass self to it. It is similar to the way you implement tableview delegate methods. – Surely Aug 21 '16 at 10:48
  • thank you Surely, everything works perfectly now! – Julio Cesar Aguilar Jiménez Aug 24 '16 at 02:30

6 Answers6

22

Hello recently I encountered the same problem and solved it this way-

class ViewController: UIViewController {

    var months: [String]!
    var unitsSold = [Double]()
    weak var axisFormatDelegate: IAxisValueFormatter?

    @IBOutlet var viewForChart: BarChartView!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        axisFormatDelegate = self
        months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
        unitsSold = [20.0, 4.0, 6.0, 3.0, 12.0, 16.0, 4.0, 18.0, 2.0, 4.0, 5.0, 4.0]

       // setChart(_ dataPoints:months,_ forY: unitsSold)
        setChart(dataEntryX: months, dataEntryY: unitsSold)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func setChart(dataEntryX forX:[String],dataEntryY forY: [Double]) {
        viewForChart.noDataText = "You need to provide data for the chart."
        var dataEntries:[BarChartDataEntry] = []
        for i in 0..<forX.count{
           // print(forX[i])
           // let dataEntry = BarChartDataEntry(x: (forX[i] as NSString).doubleValue, y: Double(unitsSold[i]))
            let dataEntry = BarChartDataEntry(x: Double(i), y: Double(forY[i]) , data: months as AnyObject?)
            print(dataEntry)
            dataEntries.append(dataEntry)
        }
        let chartDataSet = BarChartDataSet(values: dataEntries, label: "Units Sold")
        let chartData = BarChartData(dataSet: chartDataSet)
        viewForChart.data = chartData
        let xAxisValue = viewForChart.xAxis
        xAxisValue.valueFormatter = axisFormatDelegate

    }
}

At the end simply add the extension for the delegate

extension ViewController: IAxisValueFormatter {

    func stringForValue(_ value: Double, axis: AxisBase?) -> String {
    return months[Int(value)]
    }
}

Figured this solution out through this link https://medium.com/@skoli/using-realm-and-charts-with-swift-3-in-ios-10-40c42e3838c0#.minfe6kuk

svilla
  • 11
  • 3
Harshit Goel
  • 679
  • 1
  • 6
  • 20
16

I found the solution, maybe another one can solve this problem in a better way, without create a new class, well this is what I found:

First you nedd to create a new class, which will be your formatter class and add the IAxisValueFormater interface, then you use the method stringForValue to customize the new values, it takes two parameters, the first is the actual value (you can see it like the index) and second is the axis where the value is.

import UIKit
import Foundation
import Charts

@objc(BarChartFormatter)
public class BarChartFormatter: NSObject, IAxisValueFormatter
{
  var months: [String]! = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]

  public func stringForValue(value: Double, axis: AxisBase?) -> String 
  {
    return months[Int(value)]
  }   
}

Now in your file at the begining of your setChart() function you have to create two variables one for your formatter class and one for the axis class:

let formato:BarChartFormatter = BarChartFormatter()
let xaxis:XAxis = XAxis()

Next, go ahead at the end of the for in loop and pass the index and axis to your formatter variable:

formato.stringForValue(Double(i), axis: xaxis)

After looping add the format to your axis variable:

xaxis.valueFormatter = formato

The final step is to add the new xaxisformatted to the barChartView:

barChartView.xAxis.valueFormatter = xaxis.valueFormatter

And that's all the other lines in the setChart() function just keep it where they are, I hope it can help you.

Tobi
  • 137
  • 2
  • 12
  • This works well, but how would i pass month values into the class, rather than having to have them setup up front (since I am trying to get these values from CoreData) – Charlie S Oct 04 '16 at 12:54
  • In your formatter class, just add a new method for example "setArray", and it takes one array as parameter then just set your array var, in this case "months" and make it equal to your array, in your main class you can call this method "setArray" and send the array you want. – Julio Cesar Aguilar Jiménez Oct 04 '16 at 17:33
  • Great answer to your question! But do you know how to fit x-labels when you set a different bar-width to your columns? – Francisco Romero Oct 05 '16 at 09:48
  • but what's the problem with x-labels, rather tan change the bar-width you can adjust the zoom of the chartView – Julio Cesar Aguilar Jiménez Oct 05 '16 at 17:51
  • @JulioCesarAguilarJiménez - The setArray method worked for return string values, but the stringForValue() method seems to get called numerous times (from the chart code not mine) - meaning lots more date strings appear on the graph than are required. Any ideas? – Charlie S Oct 06 '16 at 12:53
  • Oh yep, the labels could be duplicated because you have to specify the granularity, use these lines: barChartView.xAxis.granularityEnabled = true barChartView.xAxis.granularity = 1.0 The granularity helps you to say what will be the space between labels if you put 2, the chart will show one label and the next will be hidden, the third will show... etc. – Julio Cesar Aguilar Jiménez Oct 06 '16 at 14:38
  • Thanks for the suggestion JulioCesarAguilarJiménez but afraid that didnt help. Dont suppose you know of an online xcode demo project written in Swift 3 that shows a bar chart with x values as strings? – Charlie S Oct 06 '16 at 15:03
  • 1
    Fixed it. The trouble was the x axis was showing 1 decimal point and every decimal point shown was replaced with a date. Fixed it by adding this line - hopefully helps others: barChartView.xAxis.decimals = 0 – Charlie S Oct 07 '16 at 15:12
  • 1
    Can i get an answer in Objective C? – Wodjefer Dec 26 '16 at 09:50
  • I used same but due to this warning Result of call to 'stringForValue(_:axis:)' is unused its crashed.please tell me how can i fixed it. – Ravikant Jan 07 '17 at 07:17
  • @Wodjefer, Have you accomplish this in objective c yet? [I am also facing the same issue](http://stackoverflow.com/questions/41892396/how-to-set-x-axis-on-bar-chart-data-ios-charts). – pkc456 Jan 27 '17 at 12:12
  • @pkc456: No buddy. I am still using the old version of Charts. – Wodjefer Jan 28 '17 at 05:26
  • @Wodjefer I am using Xcode 8 and build errors occurs because of charts library. Which Xcode and chart library version you are using. – pkc456 Jan 29 '17 at 13:54
  • 1
    @Wodjefer, I resolved the issue. Details are [available in this answer](http://stackoverflow.com/a/41932231/988169). Xcode 8+, Objective C, ios-Charts `V3.0.1` – pkc456 Jan 30 '17 at 09:19
  • formato.stringForValue(Double(i), axis: xaxis) it just returns an string!! what should we do we with this string value any explaination? @Sabrina Tuli – Mehrdad Jul 14 '19 at 07:15
13

As many people said you can create a class or extend with the type IAxisValueFormatter, but you can easily use the following method which only take 3 lines :

self.chartView.xAxis.valueFormatter = DefaultAxisValueFormatter(block: {(index, _) in
    return xStrings[Int(index)]
})

here's my setChart() function in case some of you find some of my customization interesting :

func setChartView(entriesData: [entry]) {
    var chartEntries: [ChartDataEntry] = []
    var xStrings: [String] = []
    let sortedentriesData = entriesData.sorted { (s1: entry, s2: entry) -> Bool in
        return s1.timestamp < s2.timestamp
    }
    for (i, entry) in sortedentriesData.enumerated() {
        let newEntry = ChartDataEntry(x: Double(i), y: entry.temperature)
        chartEntries.append(newEntry)
        let dateFormatter = DateFormatter()
        dateFormatter.timeStyle = .medium
        dateFormatter.dateStyle = .none
        xStrings.append("\(dateFormatter.string(from: Date.init(timeIntervalSince1970: TimeInterval(entry.timestamp))))")
    }
    let set: LineChartDataSet = LineChartDataSet(values: chartEntries, label: "°C")
    set.setColor(NSUIColor.blue, alpha: CGFloat(1))
    set.circleColors = [NSUIColor.blue]
    set.circleRadius = 3
    set.mode = LineChartDataSet.Mode.cubicBezier

    let data: LineChartData = LineChartData(dataSet: set)
    self.chartView.xAxis.labelRotationAngle = -90
    self.chartView.xAxis.valueFormatter = DefaultAxisValueFormatter(block: {(index, _) in
        return xStrings[Int(index)]
    })
    self.chartView.xAxis.setLabelCount(xStrings.count, force: true)
    self.chartView.data = data
}

entry is a struct I created :

struct entry {
    var timestamp: Int
    var temperature: Double
}
Damien
  • 3,322
  • 3
  • 19
  • 29
6

A cleaner way to implement the suggestion above if you are subclassing a graph is to implement IAxisValueFormatter protocol in your subclass itself

class CustomGraphView : LineChartView, ChartViewDelegate, IAxisValueFormatter {}

like this...

public func stringForValue(_ value: Double, axis: Charts.AxisBase?) -> String
{
    return "My X Axis Label Str";  // Return you label string, from an array perhaps.
}

Then in your init()

self.xAxis.valueFormatter = self;
RedHotPawn.com
  • 796
  • 9
  • 10
6

The most simple solution:

barChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: months)
barChartView.xAxis.granularity = 1
Zoltan Vinkler
  • 1,207
  • 1
  • 15
  • 20
3

I achieved this like that, I have date in "yyyy-MM-dd HH:mm" format and I want show "HH:mm" in the from of string in x-axis. Here is code,

I used Cubic Lines chart , first i set data on chart

func setDataCount() {

    var dataEntries: [ChartDataEntry] = []
    let formatter = CubicChartFormatter()
    let xaxis:XAxis = XAxis()
    if graphArray?.count ?? 0>0 {
        for i in 0..<graphArray!.count {
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
            let date = dateFormatter.date(from:graphArray![i].writetime)
            let formatingDate = getFormattedDate(date: date!, format: "HH:mm")
            xDataPoints.append(formatingDate)
            formatter.setValues(values: xDataPoints)
            let dataEntry = ChartDataEntry(x: Double(i), y: Double(yVal)!, data: formatingDate)
            dataEntries.append(dataEntry)
            let set1 = LineChartDataSet(entries: dataEntries, label: "DataSet")
            let data = LineChartData(dataSet: set1)
            chartView.xAxis.valueFormatter = formatter
            chartView.data = data
        }
    }
}

and Make format Class

@objc(CubicChartFormatter)
public class CubicChartFormatter: NSObject, IAxisValueFormatter
{
   var names = [String]()

   public func stringForValue(_ value: Double, axis: AxisBase?) -> String
   {
      return names[Int(value)]
   }

   public func setValues(values: [String])
   {
      self.names = values
   } 
}
Narendra Jagne
  • 436
  • 3
  • 8