Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added bar gradient #3225

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChartsDemo/Swift/DemoBaseViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ enum Option {
case toggleYLabels
case toggleRotate
case toggleHighlightCircle
// VericalBarChart
case toggleBarGradient

var label: String {
switch self {
Expand Down Expand Up @@ -81,6 +83,8 @@ enum Option {
case .toggleYLabels: return "Toggle Y-Labels"
case .toggleRotate: return "Toggle Rotate"
case .toggleHighlightCircle: return "Toggle highlight circle"
// VericalBarChart
case .toggleBarGradient: return "Toggle Bar Gradient"
}
}
}
Expand Down
18 changes: 16 additions & 2 deletions ChartsDemo/Swift/Demos/AnotherBarChartViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class AnotherBarChartViewController: DemoBaseViewController {
.saveToGallery,
.togglePinchZoom,
.toggleData,
.toggleBarBorders]
.toggleBarBorders,
.toggleBarGradient]

chartView.delegate = self

Expand Down Expand Up @@ -85,7 +86,20 @@ class AnotherBarChartViewController: DemoBaseViewController {
}

override func optionTapped(_ option: Option) {
super.handleOption(option, forChartView: chartView)
switch option {
case .toggleBarGradient:
for set in chartView.data!.dataSets as! [BarChartDataSet] {
if set.barGradientColors == nil {
set.barGradientColors = ChartColorTemplates.gradients()
set.barGradientOrientation = .horizontal
} else {
set.barGradientColors = nil
}
}
chartView.notifyDataSetChanged()
default:
super.handleOption(option, forChartView: chartView)
}
}

// MARK: - Actions
Expand Down
17 changes: 15 additions & 2 deletions ChartsDemo/Swift/Demos/BarChartViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class BarChartViewController: DemoBaseViewController {
.saveToGallery,
.togglePinchZoom,
.toggleData,
.toggleBarBorders]
.toggleBarBorders,
.toggleBarGradient]

self.setup(barLineChartView: chartView)

Expand Down Expand Up @@ -139,7 +140,19 @@ class BarChartViewController: DemoBaseViewController {
}

override func optionTapped(_ option: Option) {
super.handleOption(option, forChartView: chartView)
switch option {
case .toggleBarGradient:
for set in chartView.data!.dataSets as! [BarChartDataSet] {
if set.barGradientColors == nil {
set.barGradientColors = ChartColorTemplates.gradients()
} else {
set.barGradientColors = nil
}
}
chartView.notifyDataSetChanged()
default:
super.handleOption(option, forChartView: chartView)
}
}

// MARK: - Actions
Expand Down
17 changes: 15 additions & 2 deletions ChartsDemo/Swift/Demos/MultipleBarChartViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class MultipleBarChartViewController: DemoBaseViewController {
.togglePinchZoom,
.toggleAutoScaleMinMax,
.toggleData,
.toggleBarBorders]
.toggleBarBorders,
.toggleBarGradient]

chartView.delegate = self

Expand Down Expand Up @@ -138,7 +139,19 @@ class MultipleBarChartViewController: DemoBaseViewController {
}

override func optionTapped(_ option: Option) {
super.handleOption(option, forChartView: chartView)
switch option {
case .toggleBarGradient:
for set in chartView.data!.dataSets as! [BarChartDataSet] {
if set.barGradientColors == nil {
set.barGradientColors = ChartColorTemplates.gradients()
} else {
set.barGradientColors = nil
}
}
chartView.notifyDataSetChanged()
default:
super.handleOption(option, forChartView: chartView)
}
}

// MARK: - Actions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class PositiveNegativeBarChartViewController: DemoBaseViewController {
.togglePinchZoom,
.toggleAutoScaleMinMax,
.toggleData,
.toggleBarBorders]
.toggleBarBorders,
.toggleBarGradient]

self.setup(barLineChartView: chartView)

Expand Down Expand Up @@ -109,7 +110,19 @@ class PositiveNegativeBarChartViewController: DemoBaseViewController {
}

override func optionTapped(_ option: Option) {
super.handleOption(option, forChartView: chartView)
switch option {
case .toggleBarGradient:
for set in chartView.data!.dataSets as! [BarChartDataSet] {
if set.barGradientColors == nil {
set.barGradientColors = ChartColorTemplates.gradients()
} else {
set.barGradientColors = nil
}
}
chartView.notifyDataSetChanged()
default:
super.handleOption(option, forChartView: chartView)
}
}
}

Expand Down
17 changes: 15 additions & 2 deletions ChartsDemo/Swift/Demos/StackedBarChartViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ class StackedBarChartViewController: DemoBaseViewController {
.togglePinchZoom,
.toggleAutoScaleMinMax,
.toggleData,
.toggleBarBorders]
.toggleBarBorders,
.toggleBarGradient]


chartView.delegate = self
Expand Down Expand Up @@ -113,7 +114,19 @@ class StackedBarChartViewController: DemoBaseViewController {
}

override func optionTapped(_ option: Option) {
super.handleOption(option, forChartView: chartView)
switch option {
case .toggleBarGradient:
for set in chartView.data!.dataSets as! [BarChartDataSet] {
if set.barGradientColors == nil {
set.barGradientColors = ChartColorTemplates.gradients()
} else {
set.barGradientColors = nil
}
}
chartView.notifyDataSetChanged()
default:
super.handleOption(option, forChartView: chartView)
}
}

@IBAction func slidersValueChanged(_ sender: Any?) {
Expand Down
27 changes: 27 additions & 0 deletions Source/Charts/Data/Implementations/Standard/BarChartDataSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ import CoreGraphics

open class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, BarChartDataSetProtocol
{
@objc
public enum BarGradientOrientation: Int
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't make sense to nest this in BarChartDataSet

{
case vertical
case horizontal
}

private func initialize()
{
self.highlightColor = NSUIColor.black
Expand Down Expand Up @@ -149,6 +156,26 @@ open class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, BarChartData
/// the alpha value (transparency) that is used for drawing the highlight indicator bar. min = 0.0 (fully transparent), max = 1.0 (fully opaque)
open var highlightAlpha = CGFloat(120.0 / 255.0)

/// array of gradient colors [[color1, color2], [color3, color4]]
open var barGradientColors: [[NSUIColor]]?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a nested array?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested array is array of colors for each bar.


open var barGradientOrientation: BarChartDataSet.BarGradientOrientation = .vertical

/// - returns: The gradient colors at the given index of the DataSet's gradient color array.
/// This prevents out-of-bounds by performing a modulus on the gradient color index, so colours will repeat themselves.
open func barGradientColor(atIndex index: Int) -> [NSUIColor]?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swift naming convention would be func barGradientColor(at index: Int) -> [NSUIColor]?

{
guard let gradientColors = barGradientColors
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one line

else { return nil }

var index = index
if index < 0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessary, it is already common place in Swift and Obj-C that collections require a positive index. Array doesn't perform these checks, we don't need to either.

{
index = 0
}
return gradientColors[index % gradientColors.count]
}

// MARK: - NSCopying

open override func copyWithZone(_ zone: NSZone?) -> AnyObject
Expand Down
9 changes: 9 additions & 0 deletions Source/Charts/Data/Interfaces/BarChartDataSetProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,13 @@ public protocol BarChartDataSetProtocol: BarLineScatterCandleBubbleChartDataSetP

/// array of labels used to describe the different values of the stacked bars
var stackLabels: [String] { get set }

/// array of gradient colors [[color1, color2], [color3, color4]]
var barGradientColors: [[NSUIColor]]? { get set }

var barGradientOrientation: BarChartDataSet.BarGradientOrientation { get set }

/// - returns: The gradient colors at the given index of the DataSet's gradient color array.
/// This prevents out-of-bounds by performing a modulus on the gradient color index, so colours will repeat themselves.
func barGradientColor(atIndex index: Int) -> [NSUIColor]?
}
65 changes: 51 additions & 14 deletions Source/Charts/Renderers/BarChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -241,37 +241,74 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
}
}

let isSingleColor = dataSet.colors.count == 1

if isSingleColor
{
context.setFillColor(dataSet.color(atIndex: 0).cgColor)
}

for j in buffer.indices
{
let barRect = buffer[j]

guard viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width) else { continue }
guard viewPortHandler.isInBoundsRight(barRect.origin.x) else { break }

if !isSingleColor
{
// Set the color for the currently drawn value. If the index is out of bounds, reuse colors.
context.setFillColor(dataSet.color(atIndex: j).cgColor)
}

context.fill(barRect)
drawBar(context: context, dataSet: dataSet, index: j, barRect: barRect)

if drawBorder
{
context.saveGState()
context.setStrokeColor(borderColor.cgColor)
context.setLineWidth(borderWidth)
context.stroke(barRect)
context.restoreGState()
}
}
}

open func drawBar(context: CGContext, dataSet: BarChartDataSetProtocol, index: Int, barRect: CGRect)
{
context.saveGState()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defer { context.restoreGState() }

if let gradientColors = dataSet.barGradientColors, gradientColors.count > 0
{
if let gradientColor = dataSet.barGradientColor(atIndex: index)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the if statement? We know dataSet.barGradientColor(atIndex:) only returns nilwhendataSet.barGradientColors == nil`. It seems to me we could just write

if let gradientColor = dataSet.barGradientColor(atIndex: index) {
    drawGradient(context: context, barRect: barRect, gradientColors: gradientColor, orientation: dataSet.barGradientOrientation)
} // ...

{
drawGradient(context: context, barRect: barRect, gradientColors: gradientColor, orientation: dataSet.barGradientOrientation)
}
}
else
{
// Set the color for the currently drawn value. If the index is out of bounds, reuse colors.
let fillColor = dataSet.color(atIndex: index).cgColor
context.setFillColor(fillColor)
context.fill(barRect)
}

context.restoreGState()
}

open func drawGradient(context: CGContext, barRect: CGRect, gradientColors: Array<NSUIColor>, orientation: BarChartDataSet.BarGradientOrientation)
{
let cgColors = gradientColors.map{ $0.cgColor } as CFArray
let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: cgColors, locations: nil)

let startPoint: CGPoint
let endPoint: CGPoint

switch orientation
{
case .vertical:
startPoint = CGPoint(x: barRect.midX, y: barRect.maxY)
endPoint = CGPoint(x: barRect.midX, y: barRect.minY)

case .horizontal:
startPoint = CGPoint(x: barRect.minX, y: barRect.midY)
endPoint = CGPoint(x: barRect.maxX, y: barRect.midY)
}

let path = NSUIBezierPath.init(rect: barRect)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't actually need to define NSUIBezierPath since CGPath already has the initializer init(rect: CGRect, transform: UnsafePointer<CGAffineTransform>?) i.e.

let path = CGPath(rect: barRect, transform: nil)


context.addPath(path.cgPath)
context.clip()
context.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: [])
}

open func prepareBarHighlight(
x: Double,
y1: Double,
Expand Down
15 changes: 1 addition & 14 deletions Source/Charts/Renderers/HorizontalBarChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,6 @@ open class HorizontalBarChartRenderer: BarChartRenderer

let buffer = _buffers[index]

let isSingleColor = dataSet.colors.count == 1

if isSingleColor
{
context.setFillColor(dataSet.color(atIndex: 0).cgColor)
}

for j in stride(from: 0, to: buffer.rects.count, by: 1)
{
let barRect = buffer.rects[j]
Expand All @@ -255,13 +248,7 @@ open class HorizontalBarChartRenderer: BarChartRenderer
continue
}

if !isSingleColor
{
// Set the color for the currently drawn value. If the index is out of bounds, reuse colors.
context.setFillColor(dataSet.color(atIndex: j).cgColor)
}

context.fill(barRect)
drawBar(context: context, dataSet: dataSet, index: j, barRect: barRect)

if drawBorder
{
Expand Down
12 changes: 12 additions & 0 deletions Source/Charts/Utils/ChartColorTemplates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ open class ChartColorTemplates: NSObject
]
}

@objc open class func gradients () -> [[NSUIColor]]
{
return [
[NSUIColor(red: 146/255.0, green: 132/255.0, blue: 240/255.0, alpha: 1.0),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did these colours come from? If they are from other templates, you should directly call them instead of redefining them.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's custom colours especially for the gradients.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but where did the values for the colours come from? We already have a list of themes in ChartColorTemplates. If this adds to that, it should be done there. It probably should just use the already existing theme colours though.

NSUIColor(red: 225/255.0, green: 129/255.0, blue: 222/255.0, alpha: 1.0)],
[NSUIColor(red: 253/255.0, green: 155/255.0, blue: 168/255.0, alpha: 1.0),
NSUIColor(red: 236/255.0, green: 215/255.0, blue: 144/255.0, alpha: 1.0)],
[NSUIColor(red: 98/255.0, green: 199/255.0, blue: 203/255.0, alpha: 1.0),
NSUIColor(red: 147/255.0, green: 210/255.0, blue: 173/255.0, alpha: 1.0)]
]
}

@objc open class func colorFromString(_ colorString: String) -> NSUIColor
{
let leftParenCharset: CharacterSet = CharacterSet(charactersIn: "( ")
Expand Down
Loading