I was doing a tutorial on AppCoda for iOS-charts the bar chart portion on the app worked fine but the pie chart portion crashes with the the error: fatal error: unexpectedly found nil while unwrapping a optional value. It crashes on this command pieChartView.data = pieChartData. Here is the code:
func setChart(dataPoints: [String], values: [Double]) {
var dataEntries: [ChartDataEntry] = []
for i in 0..<dataPoints.count {
let dataEntry = ChartDataEntry(value: values[i], xIndex: i)
dataEntries.append(dataEntry)
}
let pieChartDataSet = PieChartDataSet(yVals: dataEntries)
let pieChartData = PieChartData(xVals: dataPoints, dataSet: pieChartDataSet)
pieChartDataSet.label = ""
pieChartView.data = pieChartData
var colors: [UIColor] = []
colors.append(UIColor.greenColor())
colors.append(UIColor.blueColor())
colors.append(UIColor.blackColor())
pieChartDataSet.colors = colors
}
anyone have any idea why??
Related
I am using the Charts library to show bar charts but it still shows nil. I have set the view as BarChartView.
#IBOutlet weak var barChartView: BarChartView!
func setChart() {
var dataEntries: [BarChartDataEntry] = []
for i in 0..<testArray.count {
let dataEntry = BarChartDataEntry(x: Double(i), y: Double(testArray[i]))
dataEntries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(entries: dataEntries, label: "logs")
let chartData = BarChartData(dataSet: chartDataSet)
barChartView.data = chartData
}
and I do call setChart() in viewDidLoad()
For the second page of my application I need to build a chart. My second page contains a spot for user input and entering date and time settings as well. Upon running my code this is the error I get: Thread 1: EXC_BAD_ACCESS (code=1, address=0x7fff1c472a34)
on this line: self.lineChartView.data = data
This is after building. How do I set the data for the chart?
#IBOutlet weak var lineChartView: LineChartView!
override func viewDidLoad() {
super.viewDidLoad()
self.finalEmail = UserDefaults.standard.value(forKey: "username") as? String ?? ""
self.finalPassword = UserDefaults.standard.value(forKey: "password") as? String ?? ""
threshold.delegate = self
setChartValues()
}
func setChartValues(_ count : Int = 20) {
let values = (0..<count).map { (i) -> ChartDataEntry in
let val = Double(arc4random_uniform(UInt32(count)) + 3)
return ChartDataEntry(x: Double(i), y: val)
}
let set1 = LineChartDataSet(values: values, label: "DataSet 1")
let data = LineChartData(dataSet: set1)
self.lineChartView.data = data
}
It builds without errors but when I run it says: Thread 1: EXC_BAD_ACCESS (code=1, address=0x7fff1ad91d44)
How to remove decimals from y values in iOS Charts?
Im using the latest iOS Charts release with Swift3
Thanks for every one who tried to help, here was the fix, adding the below code
let formatter = NumberFormatter()
formatter.numberStyle = .none
formatter.maximumFractionDigits = 0
formatter.multiplier = 1.0
chartData.valueFormatter = DefaultValueFormatter(formatter: formatter)
to the setBarChartData func
func setBarChartData(xValues: [String], yValues: [Double], label: String) {
var dataEntries: [BarChartDataEntry] = []
for i in 0..<yValues.count {
let dataEntry = BarChartDataEntry(x: Double(i), y: yValues[i])
dataEntries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(values: dataEntries, label: label)
let chartData = BarChartData(dataSet: chartDataSet)
let formatter = NumberFormatter()
formatter.numberStyle = .none
formatter.maximumFractionDigits = 0
formatter.multiplier = 1.0
chartData.valueFormatter = DefaultValueFormatter(formatter: formatter)
let chartFormatter = BarChartFormatter(labels: xValues)
let xAxis = XAxis()
xAxis.valueFormatter = chartFormatter
self.xAxis.valueFormatter = xAxis.valueFormatter
self.data = chartData
}
Could also use:
let format = NumberFormatter()
format.numberStyle = .none
let formatter = DefaultValueFormatter(formatter: format)
data.setValueFormatter(formatter)
You need to set delegate for value formatter in DataSet like below
Obj-C :
//DataSet 1
LineChartDataSet *set1 = [[LineChartDataSet alloc] initWithValues:values label:#"outstanding"];
set1.valueFormatter = self;
Use below delegate method for formatting your value :
#pragma mark - IChartValueFormatter
- (NSString * _Nonnull)stringForValue:(double)value entry:(ChartDataEntry * _Nonnull)entry dataSetIndex:(NSInteger)dataSetIndex viewPortHandler:(ChartViewPortHandler * _Nullable)viewPortHandler{
//Format your value what you want here
return [NSString stringWithFormat:#"%0.f",value];
}
Confirm Protocol :
#interface YourViewController ()<ChartViewDelegate,IChartValueFormatter>
For Swift you need to create Extension of BarChart and use below methods in it
Swift :
extension BarChartView {
private class BarChartFormatter: NSObject, IAxisValueFormatter {
var labels: [String] = []
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return labels[Int(value)]
}
init(labels: [String]) {
super.init()
self.labels = labels
}
}
func setBarChartData(xValues: [String], yValues: [Double], label: String) {
var dataEntries: [BarChartDataEntry] = []
for i in 0..<yValues.count {
let dataEntry = BarChartDataEntry(x: Double(i), y: yValues[i])
dataEntries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(values: dataEntries, label: label)
let chartData = BarChartData(dataSet: chartDataSet)
let chartFormatter = BarChartFormatter(labels: xValues)
let xAxis = XAxis()
xAxis.valueFormatter = chartFormatter
self.xAxis.valueFormatter = xAxis.valueFormatter
self.data = chartData
}
}
Call Above Extension Method like this :
func setChart(){
let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
let unitsSold = [20.0, 4.0, 3.0, 6.0, 12.0, 16.0, 4.0, 18.0, 2.0, 4.0, 5.0, 4.0]
barChartView.setBarChartData(xValues: months, yValues: unitsSold, label: "Monthly Sales")
}
hope you will get your formatted value on line chart.
After lots of research and test. i was able to remove the decimal.
First unlock the pod chartViewBase
Remove two lines from the { } i.e 'digits' from chartViewBase pod.
Before:
if let formatter = defaultValueFormatter as? DefaultValueFormatter
{
// setup the formatter with a new number of digits
let digits = reference.decimalPlaces
formatter.decimals = digits
}
After:
if let formatter = defaultValueFormatter as? DefaultValueFormatter
{
}
It will look like following::
I am having a hard time to migrate library Charts (from Daniel Gindi) from version 2 (Swift 2.3) to 3 (Swift 3).
Basically, I can't have the x labels (dates) aligned correctly with the corresponding plots.
This is what I had before in version 2:
In version 2, I had values for days 7, 8, 10 and 11.
So I was missing a day in the middle, but the labels were correctly alined with the plots.
And here is what I have in version 3:
In version 3, the "labels" in the x axis have now been replaced by double (for dates, it's a timeInterval since 1970), and formatted via a formatter.
So, indeniably, the graph is more "correct" now, since the chart correctly extrapolates the value for the 9th, but I can't find how to put the labels under the corresponding plots.
This is my code for the x axis:
let chartView = LineChartView()
...
let xAxis = chartView.xAxis
xAxis.labelPosition = .bottom
xAxis.labelCount = entries.count
xAxis.drawLabelsEnabled = true
xAxis.drawLimitLinesBehindDataEnabled = true
xAxis.avoidFirstLastClippingEnabled = true
// Set the x values date formatter
let xValuesNumberFormatter = ChartXAxisFormatter()
xValuesNumberFormatter.dateFormatter = dayNumberAndShortNameFormatter // e.g. "wed 26"
xAxis.valueFormatter = xValuesNumberFormatter
Here is the ChartXAxisFormatter class I created:
import Foundation
import Charts
class ChartXAxisFormatter: NSObject {
var dateFormatter: DateFormatter?
}
extension ChartXAxisFormatter: IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
if let dateFormatter = dateFormatter {
let date = Date(timeIntervalSince1970: value)
return dateFormatter.string(from: date)
}
return ""
}
}
So, the values here are correct, the formatting is correct, the shape of the chart is correct, but the alignment of the labels with the corresponding plots is not good.
Thanks for your help
OK, got it!
You've got to define a reference time Interval (the "0" for the x axis). And then calculate the additional time interval for each x value.
The ChartXAxisFormatter becomes:
import Foundation
import Charts
class ChartXAxisFormatter: NSObject {
fileprivate var dateFormatter: DateFormatter?
fileprivate var referenceTimeInterval: TimeInterval?
convenience init(referenceTimeInterval: TimeInterval, dateFormatter: DateFormatter) {
self.init()
self.referenceTimeInterval = referenceTimeInterval
self.dateFormatter = dateFormatter
}
}
extension ChartXAxisFormatter: IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
guard let dateFormatter = dateFormatter,
let referenceTimeInterval = referenceTimeInterval
else {
return ""
}
let date = Date(timeIntervalSince1970: value * 3600 * 24 + referenceTimeInterval)
return dateFormatter.string(from: date)
}
}
And, then, when I create my data entries, it works like so:
// (objects is defined as an array of struct with date and value)
// Define the reference time interval
var referenceTimeInterval: TimeInterval = 0
if let minTimeInterval = (objects.map { $0.date.timeIntervalSince1970 }).min() {
referenceTimeInterval = minTimeInterval
}
// Define chart xValues formatter
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .none
formatter.locale = Locale.current
let xValuesNumberFormatter = ChartXAxisFormatter(referenceTimeInterval: referenceTimeInterval, dateFormatter: formatter)
// Define chart entries
var entries = [ChartDataEntry]()
for object in objects {
let timeInterval = object.date.timeIntervalSince1970
let xValue = (timeInterval - referenceTimeInterval) / (3600 * 24)
let yValue = object.value
let entry = ChartDataEntry(x: xValue, y: yValue)
entries.append(entry)
}
// Pass these entries and the formatter to the Chart ...
The result is much nicer (I removed cubic by the way):
If you exactly know how many labels you need in the x-axis,you can write this code to solve it.For example,If I need seven labels to appear on the x-axis,Then this should be enough.The reason is the chart library is not properly calculating the intervals between the two x-axis points and hence the plot-label mismatch.When we force the library to plot against the given number of labels,The issue seems to be gone.
let xAxis = chart.xAxis
xAxis.centerAxisLabelsEnabled = false
xAxis.setLabelCount(7, force: true) //enter the number of labels here
#IBOutlet weak var tView:UIView!
#IBOutlet weak var lineChartView:LineChartView!{
didSet{
lineChartView.xAxis.labelPosition = .bottom
lineChartView.xAxis.granularityEnabled = true
lineChartView.xAxis.granularity = 1.0
let xAxis = lineChartView.xAxis
// xAxis.axisMinimum = 0.0
// xAxis.granularity = 1.0
// xaAxis.setLabelCount(6, force: true)
}
}
#IBOutlet weak var back: UIButton?
#IBAction func back(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
self.lineChartView.delegate = self
self.lineChartView.chartDescription?.textColor = UIColor.white
let months = ["Jan" , "Feb", "Mar"]
let dollars1 = [1453.0,2352,5431]
setChart(months, values: dollars1)
}
func setChart(_ dataPoints: [String], values: [Double]) {
var dataEntries: [ChartDataEntry] = []
for i in 0 ..< dataPoints.count {
dataEntries.append(ChartDataEntry(x: Double(i), y: values[i]))
}
let lineChartDataSet = LineChartDataSet(entries: dataEntries, label: nil)
lineChartDataSet.axisDependency = .left
lineChartDataSet.setColor(UIColor.black)
lineChartDataSet.setCircleColor(UIColor.black) // our circle will be dark red
lineChartDataSet.lineWidth = 1.0
lineChartDataSet.circleRadius = 3.0 // the radius of the node circle
lineChartDataSet.fillAlpha = 1
lineChartDataSet.fillColor = UIColor.black
lineChartDataSet.highlightColor = UIColor.white
lineChartDataSet.drawCircleHoleEnabled = true
var dataSets = [LineChartDataSet]()
dataSets.append(lineChartDataSet)
let lineChartData = LineChartData(dataSets: dataSets)
lineChartView.data = lineChartData
lineChartView.rightAxis.enabled = false
lineChartView.xAxis.drawGridLinesEnabled = false
lineChartView.xAxis.labelPosition = .bottom
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
}
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
print(entry)
}
I solved this issue using this answer
https://stackoverflow.com/a/44554613/2087608
I suspected that these offsets come from adjusting X axis value to a specific time of the day in my case
Here is my code
for i in 0..<valuesViewModel.entries.count {
let dataEntry = ChartDataEntry(x: roundDate(date: valuesViewModel.entries[i].date).timeIntervalSince1970, y: valuesViewModel.entries[i].value)
dataEntries.append(dataEntry)
}
func roundDate(date: Date) -> Date {
var comp: DateComponents = Calendar.current.dateComponents([.year, .month, .day], from: date)
comp.timeZone = TimeZone(abbreviation: "UTC")!
let truncated = Calendar.current.date(from: comp)!
return truncated
}
I am trying to create a lineChart ontop of a Bar chart. I have the following code so far:
class AnalysisViewController: UIViewController, ChartViewDelegate {
func setChart(xValues: [String], valuesBarChart: [Double], valuesLineChart: [Double]) {
barChartView.descriptionText = ""
barChartView.noDataText = "You need to provide data for the chart."
var yValsBarChart: [BarChartDataEntry] = []
var yValsLineChart : [ChartDataEntry] = [ChartDataEntry]()
for i in 0..<xValues.count {
yValsBarChart.append(BarChartDataEntry(value: valuesBarChart[i], xIndex: i))
yValsLineChart.append(ChartDataEntry(value: valuesLineChart[i] - 1, xIndex: i))
}
let lineChartDataSet = LineChartDataSet(yVals: yValsLineChart, label: nil)
let barChartDataSet = BarChartDataSet(yVals: yValsBarChart, label: nil)
let data: CombinedChartData = CombinedChartData(xVals: xValues)
data.barData = BarChartData(xVals: xValues, dataSets: [barChartDataSet])
data.lineData = LineChartData(xVals: xValues, dataSets: [lineChartDataSet])
barChartView.data = data
barChartView.leftAxis.customAxisMin = 0
UniversalStatic.data.generateColoursForGraph(budgetName.count)
barChartDataSet.colors = UniversalStatic.data.colourArrayForGraph
setDesignChart()
}
}
I am getting the following error on the terminal:
Could not cast value of type 'Charts.CombinedChartData' (0x3d5d70) to 'Charts.BarChartData' (0x3cfc54).
as well as:
I don't really know what i have done wrong as I haven't touched the 'BarChartView' where the error is appearing.
Any help would be appreciated :)
As i initially created the BarChart first and then tried to add the line chart, i forgot to change the view from a BarChartView to a CombinedChartView!