IOS 网格线的图表定位
IOS Charts positioning of gridlines
我正在努力寻找如何将图表上的网格线设置为 x 轴上的特定点,以及如何将标签置于对应于一天中时间的线上。我有一个简单的图表,数据代表一天大约有 280 个数据点(每 5 分钟一个数据点)。我想在一天中等于 03:00、06:00、12:00、15:00、18:00 和 21:00 的时间显示网格线并显示相应的标签在网格线下方居中。我也尝试过 LimitLines,它可以将垂直线放置在正确的点,但似乎无法将时间标签与限制线的中心对齐。
我知道这是可以做到的,因为我在网上看到屏幕截图显示了当天特定时间段以网格线(或限制线)为中心的时间标签,但我花了几个小时寻找如何做到这一点我正在画空白。
下面是我的代码(请注意注释掉的试图让限制线起作用的失败尝试)。
let tideHeights = LineChartDataSet(entries: dataEntries)
tideHeights.drawCirclesEnabled = false
tideHeights.colors = [NSUIColor.black]
//let timings = ["00:00", "03:00", "06:00", "12:00", "15:00", "18:00", "21:00"]
let data = LineChartData(dataSet: tideHeights)
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
//lineChartView.xAxis.drawGridLinesEnabled = false
//let gridLine = values.count / timings.count
//var xPos = 0
//for label in timings {
//let limitLine = ChartLimitLine(limit: Double(xPos), label: label)
//limitLine.labelPosition = .bottomLeft
//limitLine.lineWidth = 0.5
//limitLine.lineColor = .black
//limitLine.valueTextColor = .black
//lineChartView.xAxis.addLimitLine(limitLine)
//xPos += gridLine
//}
lineChartView.xAxis.labelCount = 7
lineChartView.xAxis.labelPosition = .bottom
lineChartView.xAxis.granularity = 1
lineChartView.xAxis.avoidFirstLastClippingEnabled = true
lineChartView.legend.enabled = false
lineChartView.data = data
输出如下,网格线在系统设置的位置,如何控制它们的放置位置?
更新:
在应用了 Stephan Schlecht 非常有用的建议后,我仍然得到奇怪的结果。使用 lineChartView.xAxis.setLabelCount(9, force: true)
我得到:
和 lineChartView.xAxis.setLabelCount(4, force: true)
我得到:
似乎由于某种原因忽略了 Axis 之后的第一条网格线。
代码是:
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
let marker = ChartMarker()
marker.setMinuteInterval(interval: 5)
marker.chartView = lineChartView
lineChartView.marker = marker
lineChartView.xAxis.setLabelCount(4, force: true)
lineChartView.xAxis.avoidFirstLastClippingEnabled = true
lineChartView.xAxis.labelPosition = .bottom
lineChartView.leftAxis.labelPosition = .insideChart
lineChartView.rightAxis.drawLabelsEnabled = false
lineChartView.legend.enabled = false
lineChartView.data = data
lineChartView.data?.highlightEnabled = true
更新 2
为了重现,我有以下创建数据的 class(顺便说一句,它是一个存根 class,最终它将包含一个付费的 api,所以现在我生成用于测试目的的数据:
class TideHeights {
var tideHeights = [Double]()
var tideTimes = [Date]()
var strTimes = [String]()
var intervalMins = 5
init (intervalMins: Int) {
self.intervalMins = intervalMins
let slots = 24 * 60.0 / Double(intervalMins)
let seconds = 24 * 60 * 60.0 / slots
let radians = 2 * Double.pi / slots
let endDate = Date().endOfDay
var date = Date().startOfDay
var radianOffset = 0.0
repeat {
tideHeights.append(sin(radianOffset) + 1)
tideTimes.append(date)
let timeFormatter = DateFormatter()
timeFormatter.dateFormat = "HH:mm"
strTimes.append(timeFormatter.string(from: date))
date = date.advanced(by: seconds)
radianOffset += radians
} while date <= endDate
}
}
这个class在ViewController.
下面两个函数中被实例化用来画图
func drawChart() -> Void {
let tideData = TideHeights(intervalMins: 5)
lineChartView.dragEnabled = true
lineChartView.setScaleEnabled(false)
lineChartView.pinchZoomEnabled = false
setChartValues(dataPoints: tideData.strTimes, values: tideData.tideHeights)
}
func setChartValues(dataPoints: [String], values: [Double]) -> Void {
//initialise and load array of chart data entries for drawing
var dataEntries: [ChartDataEntry] = []
for i in 0..<values.count {
let dataEntry = ChartDataEntry(x: Double(i), y: values[i])
dataEntries.append(dataEntry)
}
let tideHeights = LineChartDataSet(entries: dataEntries)
tideHeights.drawCirclesEnabled = false
tideHeights.colors = [NSUIColor.black]
let gradientColors = [ChartColorTemplates.colorFromString("#72E700").cgColor,
ChartColorTemplates.colorFromString("#724BEB").cgColor]
let gradient = CGGradient(colorsSpace: nil, colors: gradientColors as CFArray, locations: nil)!
tideHeights.fillAlpha = 0.7
tideHeights.fill = Fill(linearGradient: gradient, angle: 90)
tideHeights.drawFilledEnabled = true
let data = LineChartData(dataSet: tideHeights)
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
let marker = ChartMarker()
marker.setMinuteInterval(interval: 5)
marker.chartView = lineChartView
lineChartView.marker = marker
lineChartView.xAxis.setLabelCount(4, force: true)
lineChartView.xAxis.avoidFirstLastClippingEnabled = true
lineChartView.xAxis.labelPosition = .bottom
lineChartView.leftAxis.labelPosition = .insideChart
lineChartView.rightAxis.drawLabelsEnabled = false
lineChartView.legend.enabled = false
lineChartView.data = data
lineChartView.data?.highlightEnabled = true
}
文档说:
setLabelCount(int count, boolean force): Sets the number of labels for
the y-axis. Be aware that this number is not fixed (if force == false)
and can only be approximated. If force is enabled (true), then the
exact specified label-count is drawn – this can lead to uneven numbers
on the axis.
见https://weeklycoding.com/mpandroidchart-documentation/axis-general/
备注:虽然函数文档中提到了y轴,但实际上它可以应用于两个轴。
所以不用
lineChartView.xAxis.labelCount = 7
使用
lineChartView.xAxis.setLabelCount(9, force: true)
注意:您需要 9 个标签计数而不是 7 个。
测试
如果将一个 288 点的正弦波与上面的代码结合起来
for i in 0...288 {
let val = sin(Double(i) * 2.0 * Double.pi / 288.0) + 1
dataEntries.append(ChartDataEntry(x: Double(i), y: val))
}
并强制标签 count = 9 如图所示,那么它看起来像这样:
斯蒂芬对这个问题的回答是正确的。要强制网格线和对齐,请使用 setLabelCount(x, force: true)
与我的特定项目相关的额外复杂性以及标签中包含的数据。
我正在努力寻找如何将图表上的网格线设置为 x 轴上的特定点,以及如何将标签置于对应于一天中时间的线上。我有一个简单的图表,数据代表一天大约有 280 个数据点(每 5 分钟一个数据点)。我想在一天中等于 03:00、06:00、12:00、15:00、18:00 和 21:00 的时间显示网格线并显示相应的标签在网格线下方居中。我也尝试过 LimitLines,它可以将垂直线放置在正确的点,但似乎无法将时间标签与限制线的中心对齐。
我知道这是可以做到的,因为我在网上看到屏幕截图显示了当天特定时间段以网格线(或限制线)为中心的时间标签,但我花了几个小时寻找如何做到这一点我正在画空白。
下面是我的代码(请注意注释掉的试图让限制线起作用的失败尝试)。
let tideHeights = LineChartDataSet(entries: dataEntries)
tideHeights.drawCirclesEnabled = false
tideHeights.colors = [NSUIColor.black]
//let timings = ["00:00", "03:00", "06:00", "12:00", "15:00", "18:00", "21:00"]
let data = LineChartData(dataSet: tideHeights)
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
//lineChartView.xAxis.drawGridLinesEnabled = false
//let gridLine = values.count / timings.count
//var xPos = 0
//for label in timings {
//let limitLine = ChartLimitLine(limit: Double(xPos), label: label)
//limitLine.labelPosition = .bottomLeft
//limitLine.lineWidth = 0.5
//limitLine.lineColor = .black
//limitLine.valueTextColor = .black
//lineChartView.xAxis.addLimitLine(limitLine)
//xPos += gridLine
//}
lineChartView.xAxis.labelCount = 7
lineChartView.xAxis.labelPosition = .bottom
lineChartView.xAxis.granularity = 1
lineChartView.xAxis.avoidFirstLastClippingEnabled = true
lineChartView.legend.enabled = false
lineChartView.data = data
输出如下,网格线在系统设置的位置,如何控制它们的放置位置?
更新:
在应用了 Stephan Schlecht 非常有用的建议后,我仍然得到奇怪的结果。使用 lineChartView.xAxis.setLabelCount(9, force: true)
我得到:
和 lineChartView.xAxis.setLabelCount(4, force: true)
我得到:
似乎由于某种原因忽略了 Axis 之后的第一条网格线。
代码是:
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
let marker = ChartMarker()
marker.setMinuteInterval(interval: 5)
marker.chartView = lineChartView
lineChartView.marker = marker
lineChartView.xAxis.setLabelCount(4, force: true)
lineChartView.xAxis.avoidFirstLastClippingEnabled = true
lineChartView.xAxis.labelPosition = .bottom
lineChartView.leftAxis.labelPosition = .insideChart
lineChartView.rightAxis.drawLabelsEnabled = false
lineChartView.legend.enabled = false
lineChartView.data = data
lineChartView.data?.highlightEnabled = true
更新 2
为了重现,我有以下创建数据的 class(顺便说一句,它是一个存根 class,最终它将包含一个付费的 api,所以现在我生成用于测试目的的数据:
class TideHeights {
var tideHeights = [Double]()
var tideTimes = [Date]()
var strTimes = [String]()
var intervalMins = 5
init (intervalMins: Int) {
self.intervalMins = intervalMins
let slots = 24 * 60.0 / Double(intervalMins)
let seconds = 24 * 60 * 60.0 / slots
let radians = 2 * Double.pi / slots
let endDate = Date().endOfDay
var date = Date().startOfDay
var radianOffset = 0.0
repeat {
tideHeights.append(sin(radianOffset) + 1)
tideTimes.append(date)
let timeFormatter = DateFormatter()
timeFormatter.dateFormat = "HH:mm"
strTimes.append(timeFormatter.string(from: date))
date = date.advanced(by: seconds)
radianOffset += radians
} while date <= endDate
}
}
这个class在ViewController.
下面两个函数中被实例化用来画图func drawChart() -> Void {
let tideData = TideHeights(intervalMins: 5)
lineChartView.dragEnabled = true
lineChartView.setScaleEnabled(false)
lineChartView.pinchZoomEnabled = false
setChartValues(dataPoints: tideData.strTimes, values: tideData.tideHeights)
}
func setChartValues(dataPoints: [String], values: [Double]) -> Void {
//initialise and load array of chart data entries for drawing
var dataEntries: [ChartDataEntry] = []
for i in 0..<values.count {
let dataEntry = ChartDataEntry(x: Double(i), y: values[i])
dataEntries.append(dataEntry)
}
let tideHeights = LineChartDataSet(entries: dataEntries)
tideHeights.drawCirclesEnabled = false
tideHeights.colors = [NSUIColor.black]
let gradientColors = [ChartColorTemplates.colorFromString("#72E700").cgColor,
ChartColorTemplates.colorFromString("#724BEB").cgColor]
let gradient = CGGradient(colorsSpace: nil, colors: gradientColors as CFArray, locations: nil)!
tideHeights.fillAlpha = 0.7
tideHeights.fill = Fill(linearGradient: gradient, angle: 90)
tideHeights.drawFilledEnabled = true
let data = LineChartData(dataSet: tideHeights)
lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
let marker = ChartMarker()
marker.setMinuteInterval(interval: 5)
marker.chartView = lineChartView
lineChartView.marker = marker
lineChartView.xAxis.setLabelCount(4, force: true)
lineChartView.xAxis.avoidFirstLastClippingEnabled = true
lineChartView.xAxis.labelPosition = .bottom
lineChartView.leftAxis.labelPosition = .insideChart
lineChartView.rightAxis.drawLabelsEnabled = false
lineChartView.legend.enabled = false
lineChartView.data = data
lineChartView.data?.highlightEnabled = true
}
文档说:
setLabelCount(int count, boolean force): Sets the number of labels for the y-axis. Be aware that this number is not fixed (if force == false) and can only be approximated. If force is enabled (true), then the exact specified label-count is drawn – this can lead to uneven numbers on the axis.
见https://weeklycoding.com/mpandroidchart-documentation/axis-general/
备注:虽然函数文档中提到了y轴,但实际上它可以应用于两个轴。
所以不用
lineChartView.xAxis.labelCount = 7
使用
lineChartView.xAxis.setLabelCount(9, force: true)
注意:您需要 9 个标签计数而不是 7 个。
测试
如果将一个 288 点的正弦波与上面的代码结合起来
for i in 0...288 {
let val = sin(Double(i) * 2.0 * Double.pi / 288.0) + 1
dataEntries.append(ChartDataEntry(x: Double(i), y: val))
}
并强制标签 count = 9 如图所示,那么它看起来像这样:
斯蒂芬对这个问题的回答是正确的。要强制网格线和对齐,请使用 setLabelCount(x, force: true)
与我的特定项目相关的额外复杂性以及标签中包含的数据。