I am trying to create an candlestick chart using Charts Framework using with Codable as JSON passing myclass can be shown as :
struct ChartDataPair: Codable {
var DateTime: String = ""
var Open: Double = 0.0
var High: Double = 0.0
var Low: Double = 0.0
var Close: Double = 0.0
}
Which creates an array of chartDataPairs as shown :
struct ChartData: Codable {
var chartDataPairs: [ChartDataPair]
}
The value that I am fetching will be shows below a bit as example :
{"chartDataPairs":
[{
"DateTime": "2018/10/1 10:00:01",
"Open": 50.05,
"High": 50.05,
"Low": 49.00,
"Close":49.00
},
{
"DateTime": "2018/10/1 10:05:02",
"Open": 51.05,
"High": 54.06,
"Low": 40.00,
"Close":45.06
},
{
"DateTime": "2018/10/1 10:10:02",
"Open": 50.05,
"High": 64.06,
"Low": 40.00,
"Close":58.06
}]
}
The data is just a sample so just wrote 3 values. Now I have to fetch only time and convert the DateTime String to Double to plot it in x-axis of the charts. For with I m using :
var dataEntries: [ChartDataEntry] = []
guard let financialData = dataChart.self else {
return
}
for chartData in financialData{
let open = chartData.Open
let close = chartData.Close
let high = chartData.High
let low = chartData.Low
let datetime = chartData.DateTime
let formatter = DateFormatter()
formatter.dateFormat = "YYYY/MM/dd HH:mm:ss"
let yourDate = formatter.date(from: datetime)
formatter.dateFormat = "HH:mm"
let myStringafd = formatter.string(from: yourDate!)
let time = myStringafd
let components = time.characters.split { $0 == ":" } .map { (x) -> Int in return Int(String(x))! }
let hours = components[0]
let minutes = components[1]
let double1 = Double("\(hours).\(minutes)")
let dataEntry = CandleChartDataEntry(x: double1! , shadowH: high, shadowL: low, open: open, close: close)
dataEntries.append(dataEntry)
}
let chartDataSet = CandleChartDataSet(values: dataEntries, label: "")
chartDataSet.axisDependency = .left
chartDataSet.drawIconsEnabled = false
chartDataSet.shadowColor = .darkGray
chartDataSet.shadowWidth = 0.7
chartDataSet.decreasingColor = .red
chartDataSet.decreasingFilled = true // fill up the decreasing field color
chartDataSet.increasingColor = UIColor(red: 122/255, green: 242/255, blue: 84/255, alpha: 1)
chartDataSet.increasingFilled = true // fill up the increasing field color
chartDataSet.neutralColor = .blue
chartDataSet.barSpace = 1.0
chartDataSet.drawValuesEnabled = false
let chartData = CandleChartData(dataSet: chartDataSet)
candlestickView.data = chartData
I know that the conversion of the time to double ins't correct as per it is needed, Here I need some help on converting the datetime to double value.
The second issue is the bar width of the candlestick, I am unable to decrease the width of the candlestick.
And I want to fill up the x-axis with the time value like HH:MM with certain intervals like 15 mins, 50 mins, 4 hrs etc.
For which I followed few questions and suggestions here in given link below :
iOS-Charts Library: x-axis labels without backing data not showing
On this issue: candlestickView.xAxisRenderer = XAxisWeekRenderer()
isn't working. It is calling for viewporthandler, x-axis and transformation.
Though I can get the custom labels from the custom IAxisValueFormatter. The interval between the two values in the x-axis is not what I wanted it to be like in 15 mins or 50 mins or 4 hrs etc.
ios Charts 3.0 - Align x labels (dates) with plots
On the above mentioned link I am unable to get the minTimeInterval
and referenceTimeInterval
Basically What I want to do here is plot the hour and minute form the string that I am fetching from the JSON in x-axis and create a custom interval in between the values of x-axis while creating the custom x-axis labels.
My chart is currently shown as :
Candlestick Chart
I have same thing to display so I used this way to display the data
let xAxis = chartView.xAxis
var dataPoints = [String]()
for i in 0 ..< arrData.count
{
let timeStampFrom = arrData[i].time
dataPoints.append(self.stringFromTimestamp(timeStampFrom, strDateFormat: "h a"))
}
xAxis.valueFormatter = IndexAxisValueFormatter(values:dataPoints)
xAxis.setLabelCount(5, force: false)
Some needed function
func stringFromTimestamp(_ timeInterval : Double ,strDateFormat : String)->String
{
let dateFormatter = DateFormatter()
self.setDateFormat(dateFormatter,dateFormat: strDateFormat)
let date = Date(timeIntervalSince1970: TimeInterval(timeInterval))
return dateFormatter.string(from: date)
}
here is the output
Related
I am trying to draw line chart in my project using SwiftChart pod. I am getting response from API as ["activity_count":0 , "date": "2020-10-31"] but graph works on double values so I don't know How I draw the chart.
import UIKit
import SwiftChart
class AnalyticsViewController: UIViewController {
#IBOutlet weak var graphContainer: UIView!
var graphData = [(x:0, y: 0.0)]
var graphpResponseData = [[String: Any]]()
override func viewDidLoad() {
super.viewDidLoad()
graphpResponseData.append(["activity_count":0 , "date": "2020-10-31"])
graphpResponseData.append(["activity_count":2 , "date": "2020-10-30"])
graphpResponseData.append(["activity_count":1 , "date": "2020-10-29"])
graphpResponseData.append(["activity_count":29 , "date": "2020-10-28"])
graphpResponseData.append(["activity_count":1 , "date": "2020-10-27"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-26"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-25"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-24"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-23"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-22"])
???? unable to proceed
//let chart = Chart(frame: CGRect(x: 0, y: 0, width: 300, height: 100))
//let series = ChartSeries(data: graphData)
//chart.add(series)
//chart.xLabels = [1, 2, 3, 4]
//chart.xLabelsFormatter = { String(Int(round($1))) + "Oct" }
//graphContainer.addSubview(chart)
}
}
desired output
I just had to solve this issue. I was using the day of the month of each date in memory for the x-axis, but it turned out that this would cause the graph to act unexpectedly and show randomly plotted points when the month changed.
I also tried using timestamps for x (of type Double, which would be accepted by the ChartSeries's data initializer, but this failed too.
The solution I found was to use an increment of one for each point on the x-axis, and using an array of [Date] whose indices corresponded with the order of the datapoints in your chart's data property (for the sake of example, call this correspondingDates). Inside of chart.xLabelsFormatter, I'd access this array using correspondingDates[value]. From there, I used two dateFormatter extensions to access the values.
Here's the code:
chart.xLabelsFormatter = { (index,value) in
//Format for label
let date = correspondingDates[Int(value)]
let dayAbbr = DateFormatter.getDay(date: date)
let monthAbbr = DateFormatter.getMonth(date: date)
return monthAbbr + "-" + dayAbbr
Extensions for DateFormatter:
extension DateFormatter{
/// Used specifically to get the Month Abrreviation (ie Feb), but can be used for formatting in other ways.
/// - Parameter format: Can be 'LLL' for the month abbreviated to 3 letters, or something like 'D/MM/Y'
/// - Returns: Returns the String version of the date, with precision at any level
static func getMonth(format:String = "LLL",date: Date = Date())->String{
let formatter = DateFormatter()
let today = date // formatter.da(from: Calendar.current.dateComponents([.day], from: Date()))
formatter.dateFormat = format
let string = formatter.string(from: today)
return string
}
/// Used specifically to get the Day Abrreviation (ie 05 or 27), but can be used for formatting in other ways.
/// - Parameters:
/// - format: Can be 'LLL' for the month abbreviated to 3 letters, or something like 'D/MM/Y'
/// - date: optionally pass in your own date
/// - Returns: Returns the String version of the day, with precision at any level
static func getDay(format:String = "dd",date: Date = Date())->String{
let formatter = DateFormatter()
let today = date // formatter.da(from: Calendar.current.dateComponents([.day], from: Date()))
formatter.dateFormat = format
let string = formatter.string(from: today)
return string
}
}
Hi so I have a class Calculations with a series of functions one of these is keplerianElementsToEcef. In my view controller I hard code the values for the parameters and then call the function. However later on in a seperate class I have a bool isInRange. If my spacecraft is out of cellular range, I return false and a string as well. I also want to then iterate through the keplerianElementsToEcef function, each time increasing the timeOfCalculation parameter by two minutes until at some point in time in the future the satellite is in range.
I've tried to simply call the function but increase the value used initially as the time, current time, by two minutes. The other variables rangeMeanMotion etc, are the same as those hardcoded in the view controller
var isInRange: Bool
var rangeString: String
if distance < range {
isInRange = true
rangeString = "In Range"
} else {
isInRange = false
rangeString = "Not In Range"
while isInRange == false {
var dateString = dateFormatter.date(from: calculationTime!)!
var updatedDate = dateString.addingTimeInterval(TimeInterval(5.0 * 60.0))
var updateDateAsString = dateFormatter.string(from: updatedDate)
Calculations.shared.keplerianElementsToECEF(meanMotion: rangeMeanMotion, eccentricity: rangeEccentricity, Inclination: rangeInclination, LongitudeAscendingNode: rangeLongitudeAscendingNode, argumentPerigee: rangeArgumentPerigee, M0: rangeM0, epoch: rangeEpoch, date: updateDateAsString) {
}
}
}
In the function parameters under date: updateDateAsString I get the following error: Extra argument 'date' in call
var timeOfCalculation : TimeInterval = 0
func doItUntilSpacecraftIsInRange(){
repeat {
timeOfCalculation += TimeInterval(2.0 * 60.0)
Calculations.shared.keplerianElementsToECEF(meanMotion: rangeMeanMotion, eccentricity: rangeEccentricity, Inclination: rangeInclination, LongitudeAscendingNode: rangeLongitudeAscendingNode, argumentPerigee: rangeArgumentPerigee, M0: rangeM0, epoch: rangeEpoch, date: updateDateAsString)
} while spacecraft.isInRange == false
}
doItUntilSpacecraftIsInRange()
I solved this issue. I made the statement iterate during a certain time period (1 day) and my code looks like this:
else {
isInRange = false
rangeString = "Not In Range"
print(calculationTime)
if let calcTime = calculationTime {
let parsedDate = dateFormatter.date(from: calcTime) ?? Date()
for interval in stride(from: 0, to: 1440, by: 2) {
var updatedDate = parsedDate.addingTimeInterval(TimeInterval(interval * 60))
var updateDateAsString = dateFormatter.string(from: updatedDate)
Calculations.shared.keplerianElementsToECEF(meanMotion: rangeMeanMotion, eccentricity: rangeEccentricity, Inclination: rangeInclination, LongitudeAscendingNode: rangeLongitudeAscendingNode, argumentPerigee: rangeArgumentPerigee, M0: rangeM0, epoch: rangeEpoch, date: updateDateAsString)
let xDistance = ecefX - wgs84X
let yDistance = ecefY - wgs84Y
let zDistance = ecefZ - wgs84Z
let iteratedDistance = sqrt(xDistance*xDistance + yDistance*yDistance + zDistance*zDistance)
if iteratedDistance < 7000 {
nextVisible = updateDateAsString
break
}
}
}
}
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
}
...}
I'm creating a chart using SwiftCharts based on the Trendline example provided with the library. In my application, I'm charting weight lifting events showing the single highest lift weight on each distinct day. In my example, I'm charting 4 Bench Press events.
The chart renders when the view controller is first displayed, but when I pan to the right it stutters, spreads out, and the points move. This is what that looks like:
Note that when the chart is first rendered, the lowest plotted value (208.0) appears to be between Mar 12 and Mar 13, yet after it does that stutter thing, the point moves to the right of Mar 13.
The chartPoints being plotted are:
▿ 4 elements
▿ 0 : Mar 12, 239.17
▿ 1 : Mar 13, 208
▿ 2 : Mar 15, 267.17
▿ 3 : Mar 18, 240
Here's the view controller:
import UIKit
import SwiftCharts
class ChartViewController: UIViewController {
fileprivate var chart: Chart? // arc
fileprivate let dataManager = CoreDataHelper()
override func viewDidLoad() {
super.viewDidLoad()
let labelSettings = ChartLabelSettings(font: ChartDefaults.labelFont)
let liftEventTypeUuid = "98608870-E3CE-476A-B1E4-018D2AE4BDBF"
let liftEvents = dataManager.fetchLiftsEventsOfType(liftEventTypeUuid)
let dailyLiftEvents = liftEvents.reduce(into: [Date:[LiftEvent]](), { dailyLiftEvents, currentLiftEvent in
dailyLiftEvents[Calendar.current.startOfDay(for: currentLiftEvent.date), default: [LiftEvent]()].append(currentLiftEvent)
})
let dailyMaxLiftEvents = dailyLiftEvents.map({$0.value.max(by: {$0.oneRepMax < $1.oneRepMax})})
var sortedLiftEvents = dailyMaxLiftEvents.sorted(by: { $0?.date.compare(($1?.date)!) == .orderedAscending })
var readFormatter = DateFormatter()
readFormatter.dateFormat = "dd.MM.yyyy"
var displayFormatter = DateFormatter()
displayFormatter.dateFormat = "MMM dd"
let date = {(str: String) -> Date in
return readFormatter.date(from: str)!
}
let calendar = Calendar.current
let dateWithComponents = {(day: Int, month: Int, year: Int) -> Date in
var components = DateComponents()
components.day = day
components.month = month
components.year = year
return calendar.date(from: components)!
}
func filler(_ date: Date) -> ChartAxisValueDate {
let filler = ChartAxisValueDate(date: date, formatter: displayFormatter)
filler.hidden = true
return filler
}
let chartPoints = sortedLiftEvents.map { ChartPoint(x: ChartAxisValueDate(date: ($0?.date)!, formatter: displayFormatter), y: ChartAxisValueDouble(($0?.calculateOneRepMax().value)!)) }
let highestWeight = sortedLiftEvents.last??.oneRepMax.value
let lowestWeight = sortedLiftEvents.first??.oneRepMax.value
let yValues = stride(from: roundToTen(x: lowestWeight! - 40), through: roundToTen(x: highestWeight! + 40), by: 20).map {ChartAxisValueDouble(Double($0), labelSettings: labelSettings)}
let xGeneratorDate = ChartAxisValuesGeneratorDate(unit: .day, preferredDividers: 2, minSpace: 1, maxTextSize: 12)
let xLabelGeneratorDate = ChartAxisLabelsGeneratorDate(labelSettings: labelSettings, formatter: displayFormatter)
let firstDate = sortedLiftEvents.first??.date
let lastDate = sortedLiftEvents.last??.date
let xModel = ChartAxisModel(firstModelValue: (firstDate?.timeIntervalSince1970)!, lastModelValue: (lastDate?.timeIntervalSince1970)!, axisTitleLabels: [ChartAxisLabel(text: "Date", settings: labelSettings)], axisValuesGenerator: xGeneratorDate, labelsGenerator: xLabelGeneratorDate)
let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "1-RM", settings: labelSettings.defaultVertical()))
let chartFrame = ChartDefaults.chartFrame(view.bounds)
var chartSettings = ChartDefaults.chartSettingsWithPanZoom // was ChartDefaults.chartSettings
chartSettings.trailing = 80
// Set a fixed (horizontal) scrollable area 2x than the original width, with zooming disabled.
chartSettings.zoomPan.maxZoomX = 2
chartSettings.zoomPan.minZoomX = 2
chartSettings.zoomPan.minZoomY = 1
chartSettings.zoomPan.maxZoomY = 1
ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(chartPoints, minSegmentCount: 10, maxSegmentCount: 20, multiple: 2, axisValueGenerator: {ChartAxisValueDouble($0, labelSettings: labelSettings)}, addPaddingSegmentIfEdge: false)
let lineModel = ChartLineModel(chartPoints: chartPoints, lineColor: UIColor.red, lineCap: .round ,animDuration: 1, animDelay: 0)
let trendLineModel = ChartLineModel(chartPoints: TrendlineGenerator.trendline(chartPoints), lineColor: UIColor.blue, animDuration: 0.5, animDelay: 1)
let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel)
let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame)
let chartPointsLineLayer = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel])
let trendLineLayer = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [trendLineModel])
let settings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.black, linesWidth: ChartDefaults.guidelinesWidth)
let guidelinesLayer = ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: settings)
let chart = Chart(
frame: chartFrame,
innerFrame: innerFrame,
settings: chartSettings,
layers: [
xAxisLayer,
yAxisLayer,
guidelinesLayer,
chartPointsLineLayer,
trendLineLayer
]
)
view.addSubview(chart.view)
self.chart = chart
}
private struct liftWeightItem {
let number: Int
let text: String
init(number: Int, text: String) {
self.number = number
self.text = text
}
}
private struct liftDateItem {
let name: String
let quantity: liftWeightItem
init(name: String, quantity: liftWeightItem) {
self.name = name
self.quantity = quantity
}
}
func createChartPoint(dateStr: String, percent: Double, readFormatter: DateFormatter, displayFormatter: DateFormatter) -> ChartPoint {
return ChartPoint(x: createDateAxisValue(dateStr, readFormatter: readFormatter, displayFormatter: displayFormatter), y: ChartAxisValueDouble(percent))
}
func createDateAxisValue(_ dateStr: String, readFormatter: DateFormatter, displayFormatter: DateFormatter) -> ChartAxisValue {
let date = readFormatter.date(from: dateStr)!
let labelSettings = ChartLabelSettings(font: ChartDefaults.labelFont, rotation: 45, rotationKeep: .top)
return ChartAxisValueDate(date: date, formatter: displayFormatter, labelSettings: labelSettings)
}
func roundToTen(x : Double) -> Int {
return 10 * Int(round(x / 10.0))
}
class ChartAxisValuePercent: ChartAxisValueDouble {
override var description: String {
return "\(formatter.string(from: NSNumber(value: scalar))!)%"
}
}
}
My suspicion is that it has something to do with the rendering of the x-axis so I've tried adjusting the preferredDividers and minSpace values:
let xGeneratorDate = ChartAxisValuesGeneratorDate(unit: .day, preferredDividers: 2, minSpace: 1, maxTextSize: 12)
but that not only doesn't fix the problem, it results in a new one I'll post about separately.
I've read through the documentation several times but I still haven't been able to figure it out. This is a beautifully written library so I'd really like to be able to use it. Any help is greatly appreciated.
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.