Swift iOS LineChart с разными цветами на основе значений
Я использую библиотеку диаграмм для создания диаграмм в своем приложении. Мне нужно создать диаграмму, подобную следующей.
Насколько я пытался, мне удалось получить следующее.
Я искал день, чтобы найти решение, но не могу найти решение. Так что если кто-то из вас знает, как это исправить или у вас есть идеи по этому поводу, пожалуйста, поделитесь со мной. Благодарю.
2 ответа
Мне удалось добиться после долгих поисков. Я добился этого, отредактировав файлы библиотеки. Смотрите запрос на извлечение на GitHub. Пожалуйста, измените файлы LineChartDataSet.swift, ILineChartDataSet.swift, LineChartRenderer.swift
как показано в Pull Request
для достижения этой цели.
Для градиентной линии вы можете обновить библиотеку диаграмм до версии 4.0.0 или выше. Или, если вы не хотите обновляться, вы можете создать собственное средство визуализации линейных диаграмм. Я сделал один для рисования градиентной линии на линейной диаграмме. Не стесняйтесь использовать его или вносить изменения.
Однако вам придется внести изменения в положение градиента, чтобы получить точные результаты. А также укажите желаемые цвета в наборе данных.
class GradientLineChartRenderer: LineChartRenderer {
var _xBounds = XBounds()
init(view: LineChartView) {
super.init(dataProvider: view, animator: view.chartAnimator, viewPortHandler: view.viewPortHandler)
}
func shouldDrawValues(forDataSet set: IChartDataSet) -> Bool
{
return set.isVisible && (set.isDrawValuesEnabled || set.isDrawIconsEnabled)
}
override func drawHorizontalBezier(context: CGContext, dataSet: ILineChartDataSet) {
guard let dataProvider = dataProvider else { return }
let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency)
let phaseY = animator.phaseY
_xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator)
let drawingColor = dataSet.colors.first!
let cubicPath = CGMutablePath()
let valueToPixelMatrix = trans.valueToPixelMatrix
if _xBounds.range >= 1
{
var prev: ChartDataEntry! = dataSet.entryForIndex(_xBounds.min)
var cur: ChartDataEntry! = prev
if cur == nil { return }
cubicPath.move(to: CGPoint(x: CGFloat(cur.x), y: CGFloat(cur.y * phaseY)), transform: valueToPixelMatrix)
for j in _xBounds.dropFirst()
{
prev = cur
cur = dataSet.entryForIndex(j)
let cpx = CGFloat(prev.x + (cur.x - prev.x) / 2.0)
cubicPath.addCurve(
to: CGPoint(
x: CGFloat(cur.x),
y: CGFloat(cur.y * phaseY)),
control1: CGPoint(
x: cpx,
y: CGFloat(prev.y * phaseY)),
control2: CGPoint(
x: cpx,
y: CGFloat(cur.y * phaseY)),
transform: valueToPixelMatrix)
}
}
context.saveGState()
if dataSet.isDrawFilledEnabled
{
let fillPath = cubicPath.mutableCopy()
drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, bounds: _xBounds)
}
let fillPath = cubicPath.mutableCopy()
drawGradientLine(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix)
context.restoreGState()
}
func drawGradientLine(context: CGContext, dataSet: ILineChartDataSet, spline: CGPath, matrix: CGAffineTransform)
{
let gradientPositions = [0,0]
let boundingBox = spline.boundingBox
.insetBy(dx: -dataSet.lineWidth / 2, dy: -dataSet.lineWidth / 2)
guard !boundingBox.isNull, !boundingBox.isInfinite, !boundingBox.isEmpty else {
return
}
let gradientStart = CGPoint(x: 0, y: boundingBox.minY)
let gradientEnd = CGPoint(x: 0, y: boundingBox.maxY)
let gradientColorComponents: [CGFloat] = dataSet.colors
.reversed()
.reduce(into: []) { (components, color) in
guard let (r, g, b, a) = color.nsuirgba else {
return
}
components += [r, g, b, a]
}
let gradientLocations: [CGFloat] = gradientPositions
.map { (position) in
let location = CGPoint(x: boundingBox.minX, y: CGFloat(position))
.applying(matrix)
let normalizedLocation = (location.y - boundingBox.minY)
/ (boundingBox.maxY - boundingBox.minY)
return normalizedLocation.clamped(to: 0...1)
}
let baseColorSpace = CGColorSpaceCreateDeviceRGB()
guard let gradient = CGGradient(
colorSpace: baseColorSpace,
colorComponents: gradientColorComponents,
locations: gradientLocations,
count: gradientLocations.count) else {
return
}
context.saveGState()
defer { context.restoreGState() }
context.beginPath()
context.addPath(spline)
context.replacePathWithStrokedPath()
context.clip()
context.drawLinearGradient(gradient, start: gradientStart, end: gradientEnd, options: [])
}
}
extension NSUIColor
{
var nsuirgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)? {
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
guard getRed(&red, green: &green, blue: &blue, alpha: &alpha) else {
return nil
}
return (red: red, green: green, blue: blue, alpha: alpha)
}
}
extension Comparable
{
func clamped(to range: ClosedRange<Self>) -> Self
{
if self > range.upperBound
{
return range.upperBound
}
else if self < range.lowerBound
{
return range.lowerBound
}
else
{
return self
}
}
}
Чтобы использовать средство визуализации, используйте это, где chartView - это ваш LineChartView.
chartView.renderer = GradientLineChartRenderer(
view: chartView
)