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

Bar chart bar value outline #4432

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions Charts.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
146EE16342C2BADC92E45BF2 /* LineScatterCandleRadarChartDataSetProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9249AD9AEC8C85772365A128 /* LineScatterCandleRadarChartDataSetProtocol.swift */; };
17E994DA88777AA1D8CCFC58 /* BarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31AA65EA27776F8C653C7E8 /* BarChartDataSet.swift */; };
219192CA6B4895319AB49DCA /* BarLineScatterCandleBubbleRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C588E9DF6FFD56D7ADF8E /* BarLineScatterCandleBubbleRenderer.swift */; };
21E5B0C72586949100A44F37 /* Platform+Geometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21E5B0C62586949100A44F37 /* Platform+Geometry.swift */; };
221CA2922588FCBC00C2DD1E /* Sequence+KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 221CA2912588FCBC00C2DD1E /* Sequence+KeyPath.swift */; };
2243BBFD1FF156EC00B49D0B /* EquatableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2243BBFB1FF156D000B49D0B /* EquatableTests.swift */; };
22786E8525CD96620051335B /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 22786E8325CD96620051335B /* Media.xcassets */; };
Expand Down Expand Up @@ -202,6 +203,7 @@
1F3D55A7E6176D52DC957D27 /* XAxisRendererHorizontalBarChart.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = XAxisRendererHorizontalBarChart.swift; path = Source/Charts/Renderers/XAxisRendererHorizontalBarChart.swift; sourceTree = "<group>"; };
2194AA554712E6BA2677F114 /* BubbleChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BubbleChartRenderer.swift; path = Source/Charts/Renderers/BubbleChartRenderer.swift; sourceTree = "<group>"; };
219BC9CEA037F897E92E45D1 /* ScatterChartDataSetProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScatterChartDataSetProtocol.swift; path = Source/Charts/Data/Interfaces/ScatterChartDataSetProtocol.swift; sourceTree = "<group>"; };
21E5B0C62586949100A44F37 /* Platform+Geometry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Platform+Geometry.swift"; path = "Source/Charts/Utils/Platform+Geometry.swift"; sourceTree = "<group>"; };
221CA2912588FCBC00C2DD1E /* Sequence+KeyPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Sequence+KeyPath.swift"; path = "Source/Charts/Utils/Sequence+KeyPath.swift"; sourceTree = "<group>"; };
2243BBFB1FF156D000B49D0B /* EquatableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EquatableTests.swift; sourceTree = "<group>"; };
22786E8325CD96620051335B /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = "<group>"; };
Expand Down Expand Up @@ -570,6 +572,7 @@
D2C26AC015E753014C7571E4 /* Tests */,
);
sourceTree = "<group>";
usesTabs = 0;
};
8C531E3F3C3DE5843C93C3DA /* Charts */ = {
isa = PBXGroup;
Expand Down Expand Up @@ -659,6 +662,7 @@
3ED23C354AFE81818D78E645 /* Platform.swift */,
97AD2D4520AF917100F9C24A /* Platform+Accessibility.swift */,
C9AA36092355F01F00C97D93 /* Platform+Color.swift */,
21E5B0C62586949100A44F37 /* Platform+Geometry.swift */,
C9F3DC252355F791000C3215 /* Platform+Gestures.swift */,
C9F3DC242355F791000C3215 /* Platform+Graphics.swift */,
C9F3DC282355FA2F000C3215 /* Platform+Touch Handling.swift */,
Expand Down Expand Up @@ -851,6 +855,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
21E5B0C72586949100A44F37 /* Platform+Geometry.swift in Sources */,
D819331DA581C7E0AC5F8CEF /* Animator.swift in Sources */,
F5A209116FAC68F5903D0B46 /* ChartAnimationEasing.swift in Sources */,
D29BBEF55C9CC90114919CD2 /* BarChartView.swift in Sources */,
Expand Down
3 changes: 2 additions & 1 deletion Charts.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"originHash" : "09b08963887ce6eac26952e8acb25256a8ec102f2b5528ae68a97fa296723712",
"pins" : [
{
"identity" : "swift-snapshot-testing",
Expand All @@ -10,5 +11,5 @@
}
}
],
"version" : 2
"version" : 3
}
19 changes: 19 additions & 0 deletions ChartsDemo-iOS/Objective-C/DemoBaseViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,25 @@ - (void)handleOption:(NSString *)key forChartView:(ChartViewBase *)chartView

[chartView setNeedsDisplay];
}

if ([key isEqualToString:@"toggleBarOutline"])
{
UIEdgeInsets inset = UIEdgeInsetsZero;
inset.right = 5;
BarChartBarValueOutline *outline1 = [[BarChartBarValueOutline alloc] initWithColor:[UIColor colorWithRed:0.85 green:0.89 blue:1 alpha:1] insets:inset];
inset.right = 3;
BarChartBarValueOutline *outline2 = [[BarChartBarValueOutline alloc] initWithColor:[UIColor colorWithRed:0.2 green:0.32 blue:0.6 alpha:1] insets:inset];

for (id<BarChartDataSetProtocol, NSObject> set in chartView.data.dataSets)
{
if ([set conformsToProtocol:@protocol(BarChartDataSetProtocol)])
{
set.barValueOutlines = @[outline1, outline2];
}
}

[chartView setNeedsDisplay];
}
}

#pragma mark - Actions
Expand Down
1 change: 1 addition & 0 deletions ChartsDemo-iOS/Objective-C/Demos/BarChartViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ - (void)viewDidLoad
@{@"key": @"toggleAutoScaleMinMax", @"label": @"Toggle auto scale min/max"},
@{@"key": @"toggleData", @"label": @"Toggle Data"},
@{@"key": @"toggleBarBorders", @"label": @"Show Bar Borders"},
@{@"key": @"toggleBarOutline", @"label": @"Toggle Bar Outline"},
];

[self setupBarLineChartView:_chartView];
Expand Down
28 changes: 28 additions & 0 deletions ChartsDemo-iOS/Swift/DemoBaseViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ enum Option {
case toggleYLabels
case toggleRotate
case toggleHighlightCircle
//BarChart
case toggleBarOutline

var label: String {
switch self {
Expand Down Expand Up @@ -91,6 +93,8 @@ enum Option {
case .toggleYLabels: return "Toggle Y-Labels"
case .toggleRotate: return "Toggle Rotate"
case .toggleHighlightCircle: return "Toggle highlight circle"
// Bar Chart
case .toggleBarOutline: return "Toggle Bar Outline"
}
}
}
Expand Down Expand Up @@ -175,6 +179,30 @@ class DemoBaseViewController: UIViewController, ChartViewDelegate {
}
}
chartView.setNeedsDisplay()

case .toggleBarOutline:
let outline1 = BarChartBarValueOutline(color:NSUIColor.magenta)
outline1.insets.top = 5
outline1.insets.left = 7
outline1.insets.bottom = 2
outline1.insets.right = 5
let outline2 = BarChartBarValueOutline(color:NSUIColor.brown)
outline2.insets.top = 4
outline2.insets.left = 3
outline2.insets.bottom = 6
outline2.insets.right = 12

for set in chartView.data!.dataSets {
if let set = set as? BarChartDataSet {
if set.barValueOutlines.isEmpty {
set.barValueOutlines = [outline1, outline2]
} else {
set.barValueOutlines = [BarChartBarValueOutline]()
}
}
}
chartView.setNeedsDisplay()

default:
break
}
Expand Down
3 changes: 2 additions & 1 deletion ChartsDemo-iOS/Swift/Demos/BarChartViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class BarChartViewController: DemoBaseViewController {
.saveToGallery,
.togglePinchZoom,
.toggleData,
.toggleBarBorders]
.toggleBarBorders,
.toggleBarOutline]

self.setup(barLineChartView: chartView)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ 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)

open var barValueOutlines = [BarChartBarValueOutline]()

// MARK: - NSCopying

open override func copy(with zone: NSZone? = nil) -> Any
Expand Down
16 changes: 16 additions & 0 deletions Source/Charts/Data/Interfaces/BarChartDataSetProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@
import Foundation
import CoreGraphics

@objc open class BarChartBarValueOutline: NSObject
{
@objc public var color: NSUIColor
@objc public var insets: NSUIEdgeInsets

@objc public init(color: NSUIColor = .black, insets: NSUIEdgeInsets = .zero)
{
self.color = color
self.insets = insets
super.init()
}
}

@objc
public protocol BarChartDataSetProtocol: BarLineScatterCandleBubbleChartDataSetProtocol
{
Expand Down Expand Up @@ -39,4 +52,7 @@ public protocol BarChartDataSetProtocol: BarLineScatterCandleBubbleChartDataSetP

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

/// the color and thickness of the bar rect-outlines. The bar rect outline is a line(s) on the edge of the value bar
var barValueOutlines: [BarChartBarValueOutline] { get set }
}
59 changes: 51 additions & 8 deletions Source/Charts/Renderers/BarChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,17 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
}

let isSingleColor = dataSet.colors.count == 1
var barFillColor: NSUIColor

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

else //fix the compiler warning about using uninitialized value
{
barFillColor = .black
}

// In case the chart is stacked, we need to accomodate individual bars within accessibilityOrdereredElements
let isStacked = dataSet.isStacked
let stackSize = isStacked ? dataSet.stackSize : 1
Expand All @@ -376,18 +381,42 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
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)
barFillColor = dataSet.color(atIndex: j)
}

context.fill(barRect)


context.saveGState()

var maxEdgeInset: NSUIEdgeInsets = .zero

for outline in dataSet.barValueOutlines
{
let newMaxEdgeInset = maxEdgeInset + outline.insets
let nextRectSize = barRect.inset(by: newMaxEdgeInset).size //if access width/height of the rect - always positive values
if nextRectSize.width < 1 || nextRectSize.height < 1
{
break
}
let outlineRect = barRect.inset(by: maxEdgeInset)
context.setFillColor(outline.color.cgColor)
context.fill(outlineRect)

maxEdgeInset = newMaxEdgeInset
}

let barRectInsideOutline = barRect.inset(by: maxEdgeInset)

context.setFillColor(barFillColor.cgColor)
context.fill(barRectInsideOutline)

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

context.restoreGState()

// Create and append the corresponding accessibility element to accessibilityOrderedElements
if let chart = dataProvider as? BarChartView
{
Expand Down Expand Up @@ -483,7 +512,21 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
{
guard let e = dataSet.entryForIndex(j) as? BarChartDataEntry else { continue }

let rect = buffer[j]
var maxEdgeInset: NSUIEdgeInsets = .zero
let barRect = buffer[j]
for outline in dataSet.barValueOutlines
{
let newMaxEdgeInset = maxEdgeInset + outline.insets
let nextRectSize = barRect.inset(by: newMaxEdgeInset).size //if access width/height of the rect - always positive values
if nextRectSize.width < 1 || nextRectSize.height < 1
{
break
}

maxEdgeInset = newMaxEdgeInset
}

let rect = barRect.inset(by:maxEdgeInset) //apply insets so the text is centered in the bar

let x = rect.origin.x + rect.size.width / 2.0

Expand Down
113 changes: 113 additions & 0 deletions Source/Charts/Utils/Platform+Geometry.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//
// Platform+Geometry.swift
// Charts
//
// Created by Van on 31.07.2020.
// A port of MPAndroidChart for iOS
// Licensed under Apache License 2.0
//
// https://github.com/danielgindi/Charts
//

// MARK: - UIKit
#if canImport(UIKit)
import UIKit

public typealias NSUIEdgeInsets = UIEdgeInsets

extension NSUIEdgeInsets: AdditiveArithmetic
{
public static func - (lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> UIEdgeInsets
{
return NSUIEdgeInsets(top: lhs.top - rhs.top,
left: lhs.left - rhs.left,
bottom: lhs.bottom - rhs.bottom,
right: lhs.right - rhs.right)
}

public static func + (lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> UIEdgeInsets
{
return NSUIEdgeInsets(top: lhs.top + rhs.top,
left: lhs.left + rhs.left,
bottom: lhs.bottom + rhs.bottom,
right: lhs.right + rhs.right)
}

public static func -= (lhs: inout UIEdgeInsets, rhs: UIEdgeInsets) {
lhs.top -= rhs.top
lhs.left -= rhs.left
lhs.bottom -= rhs.bottom
lhs.right -= rhs.right
}

public static func += (lhs: inout UIEdgeInsets, rhs: UIEdgeInsets) {
lhs.top += rhs.top
lhs.left += rhs.left
lhs.bottom += rhs.bottom
lhs.right += rhs.right
}
}

#endif

// MARK: - AppKit
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
import AppKit

public typealias NSUIEdgeInsets = NSEdgeInsets

extension NSUIEdgeInsets
{
static public let zero = NSEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}

extension NSUIEdgeInsets: Equatable
{
public static func == (lhs: NSEdgeInsets, rhs: NSEdgeInsets) -> Bool {
return lhs.top == rhs.top && lhs.left == rhs.left && lhs.bottom == rhs.bottom && lhs.right == rhs.right
}
}

extension NSUIEdgeInsets: AdditiveArithmetic
{
public static func - (lhs: NSEdgeInsets, rhs: NSEdgeInsets) -> NSEdgeInsets
{
return NSUIEdgeInsets(top: lhs.top - rhs.top,
left: lhs.left - rhs.left,
bottom: lhs.bottom - rhs.bottom,
right: lhs.right - rhs.right)
}

public static func + (lhs: NSEdgeInsets, rhs: NSEdgeInsets) -> NSEdgeInsets
{
return NSUIEdgeInsets(top: lhs.top + rhs.top,
left: lhs.left + rhs.left,
bottom: lhs.bottom + rhs.bottom,
right: lhs.right + rhs.right)
}

public static func -= (lhs: inout NSEdgeInsets, rhs: NSEdgeInsets) {
lhs.top -= rhs.top
lhs.left -= rhs.left
lhs.bottom -= rhs.bottom
lhs.right -= rhs.right
}

public static func += (lhs: inout NSEdgeInsets, rhs: NSEdgeInsets) {
lhs.top += rhs.top
lhs.left += rhs.left
lhs.bottom += rhs.bottom
lhs.right += rhs.right
}
}

extension CGRect {
func inset(by insets: NSEdgeInsets) -> CGRect
{
//https://github.com/github/Archimedes/blob/063bcccf1abc7b53871d21d790bce06718c38ec0/Archimedes/MEDEdgeInsets.m
return CGRect(origin: CGPoint(x: origin.x + insets.left, y: origin.y + insets.bottom),
size: CGSize(width: width - insets.left - insets.right, height: height - insets.top - insets.bottom))
}
}

#endif
Loading
Loading