Skip to content

Commit

Permalink
+ Bar Chart: Add rounded corners
Browse files Browse the repository at this point in the history
  • Loading branch information
mosaic-engineering committed Apr 17, 2020
1 parent 56effcf commit 3096d61
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 77 deletions.
3 changes: 2 additions & 1 deletion ChartsDemo-iOS/Swift/Demos/BarChartViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ class BarChartViewController: DemoBaseViewController {
set1 = BarChartDataSet(entries: yVals, label: "The year 2017")
set1.colors = ChartColorTemplates.material()
set1.drawValuesEnabled = false

set1.roundedCorners = [.topLeft, .topRight]
set1.cornerRadius = 4
let data = BarChartData(dataSet: set1)
data.setValueFont(UIFont(name: "HelveticaNeue-Light", size: 10)!)
data.barWidth = 0.9
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,19 @@ open class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBarChartDat
open var barShadowColor = NSUIColor(red: 215.0/255.0, green: 215.0/255.0, blue: 215.0/255.0, alpha: 1.0)

/// the width used for drawing borders around the bars. If borderWidth == 0, no border will be drawn.
open var barBorderWidth : CGFloat = 0.0
open var barBorderWidth: CGFloat = 0.0

/// the color drawing borders around the bars.
open var barBorderColor = NSUIColor.black

/// 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)

/// corners to be rounded
open var roundedCorners: UIRectCorner = []

open var cornerRadius: CGFloat = 8.0

// MARK: - NSCopying

open override func copy(with zone: NSZone? = nil) -> Any
Expand Down
5 changes: 5 additions & 0 deletions Source/Charts/Data/Interfaces/IBarChartDataSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ public protocol IBarChartDataSet: IBarLineScatterCandleBubbleChartDataSet

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

/// corners to be rounded
var roundedCorners: UIRectCorner { get set }

var cornerRadius: CGFloat { get set }
}
144 changes: 105 additions & 39 deletions Source/Charts/Renderers/BarChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,9 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
// draw the bar shadow before the values
if dataProvider.isDrawBarShadowEnabled
{
for j in stride(from: 0, to: buffer.rects.count, by: 1)
for firstIndexInBar in stride(from: 0, to: buffer.rects.count, by: 1)
{
let barRect = buffer.rects[j]
let barRect = buffer.rects[firstIndexInBar]

if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width))
{
Expand All @@ -406,53 +406,81 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
{
context.setFillColor(dataSet.color(atIndex: 0).cgColor)
}

context.setStrokeColor(borderColor.cgColor)
context.setLineWidth(borderWidth)
context.setLineCap(.square)

// In case the chart is stacked, we need to accomodate individual bars within accessibilityOrdereredElements
let isStacked = dataSet.isStacked
let stackSize = isStacked ? dataSet.stackSize : 1

for j in stride(from: 0, to: buffer.rects.count, by: 1)
for firstIndexInBar in stride(from: 0, to: buffer.rects.count, by: stackSize)
{
let barRect = buffer.rects[j]
context.saveGState()

let lastIndexInBar = firstIndexInBar + stackSize - 1


if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width))
{
continue
}
let topRectInBar = findTopRectInBar(barRects: buffer.rects,
firstIndexInBar: firstIndexInBar,
lastIndexInBar: lastIndexInBar)

let path = createBarPath(for: topRectInBar, roundedCorners: dataSet.roundedCorners, cornerRadius: dataSet.cornerRadius)

if (!viewPortHandler.isInBoundsRight(barRect.origin.x))
{
break
}
context.addPath(path.cgPath)
context.clip()

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)
for index in firstIndexInBar...lastIndexInBar {

let barRect = buffer.rects[index]

if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width))
{
continue
}

if (!viewPortHandler.isInBoundsRight(barRect.origin.x))
{
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: index).cgColor)
}

context.addRect(barRect)
context.fillPath()

if drawBorder {
context.stroke(barRect)
}

// Create and append the corresponding accessibility element to accessibilityOrderedElements
if let chart = dataProvider as? BarChartView
{
let element = createAccessibleElement(withIndex: index,
container: chart,
dataSet: dataSet,
dataSetIndex: index,
stackSize: stackSize)
{ (element) in
element.accessibilityFrame = barRect
}

accessibilityOrderedElements[index/stackSize].append(element)
}

}

context.fill(barRect)
context.restoreGState()

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

// Create and append the corresponding accessibility element to accessibilityOrderedElements
if let chart = dataProvider as? BarChartView
{
let element = createAccessibleElement(withIndex: j,
container: chart,
dataSet: dataSet,
dataSetIndex: index,
stackSize: stackSize)
{ (element) in
element.accessibilityFrame = barRect
}

accessibilityOrderedElements[j/stackSize].append(element)
context.addPath(path.cgPath)
context.strokePath()
}
}

Expand Down Expand Up @@ -806,12 +834,22 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer
y1 = e.y
y2 = 0.0
}

prepareBarHighlight(x: e.x, y1: e.positiveSum, y2: -e.negativeSum, barWidthHalf: barData.barWidth / 2.0, trans: trans, rect: &barRect)

prepareBarHighlight(x: e.x, y1: y1, y2: y2, barWidthHalf: barData.barWidth / 2.0, trans: trans, rect: &barRect)

setHighlightDrawPos(highlight: high, barRect: barRect)
var highlightRect = CGRect()
prepareBarHighlight(x: e.x, y1: y1, y2: y2, barWidthHalf: barData.barWidth / 2.0, trans: trans, rect: &highlightRect)
setHighlightDrawPos(highlight: high, barRect: highlightRect)

context.fill(barRect)
let path = createBarPath(for: barRect, roundedCorners: set.roundedCorners, cornerRadius: set.cornerRadius)

context.saveGState()

context.addPath(path.cgPath)
context.clip()
context.fill(highlightRect)

context.restoreGState()
}
}

Expand Down Expand Up @@ -901,4 +939,32 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer

return element
}

private func findTopRectInBar(barRects: [CGRect], firstIndexInBar: Int, lastIndexInBar: Int) -> CGRect {
var topRectInBar = barRects[firstIndexInBar]
if barRects[lastIndexInBar].origin.y < topRectInBar.origin.y {
topRectInBar = barRects[lastIndexInBar]
}

var height: CGFloat = 0
for index in firstIndexInBar...lastIndexInBar {
height += barRects[index].height
}

topRectInBar.size.height = height

return topRectInBar
}

/// Creates path for bar in rect with rounded corners
internal func createBarPath(for rect: CGRect, roundedCorners: UIRectCorner, cornerRadius: CGFloat) -> UIBezierPath {

let cornerRadius = cornerRadius

let path = UIBezierPath(roundedRect: rect,
byRoundingCorners: roundedCorners,
cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))

return path
}
}
126 changes: 90 additions & 36 deletions Source/Charts/Renderers/HorizontalBarChartRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,53 +243,80 @@ open class HorizontalBarChartRenderer: BarChartRenderer
{
context.setFillColor(dataSet.color(atIndex: 0).cgColor)
}

context.setStrokeColor(borderColor.cgColor)
context.setLineWidth(borderWidth)
context.setLineCap(.square)

// In case the chart is stacked, we need to accomodate individual bars within accessibilityOrdereredElements
// In case the chart is stacked, we need to accomodate individual bars within accessibilityOrderedElements
let isStacked = dataSet.isStacked
let stackSize = isStacked ? dataSet.stackSize : 1

for j in stride(from: 0, to: buffer.rects.count, by: 1)
for firstIndexInBar in stride(from: 0, to: buffer.rects.count, by: stackSize)
{
let barRect = buffer.rects[j]
context.saveGState()

if (!viewPortHandler.isInBoundsTop(barRect.origin.y + barRect.size.height))
{
break
}
let lastIndexInBar = firstIndexInBar + stackSize - 1

let leftRectInBar = findMostLeftRectInBar(barRects: buffer.rects,
firstIndexInBar: firstIndexInBar,
lastIndexInBar: lastIndexInBar)

let path = createBarPath(for: leftRectInBar, roundedCorners: dataSet.roundedCorners, cornerRadius: dataSet.cornerRadius)

if (!viewPortHandler.isInBoundsBottom(barRect.origin.y))
{
continue
}
context.addPath(path.cgPath)
context.clip()

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)
for index in firstIndexInBar...lastIndexInBar {

let barRect = buffer.rects[index]

if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width))
{
continue
}

if (!viewPortHandler.isInBoundsRight(barRect.origin.x))
{
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: index).cgColor)
}

context.addRect(barRect)
context.fillPath()

if drawBorder {
context.stroke(barRect)
}

// Create and append the corresponding accessibility element to accessibilityOrderedElements
if let chart = dataProvider as? BarChartView
{
let element = createAccessibleElement(withIndex: index,
container: chart,
dataSet: dataSet,
dataSetIndex: index,
stackSize: stackSize)
{ (element) in
element.accessibilityFrame = barRect
}

accessibilityOrderedElements[index/stackSize].append(element)
}

}

context.fill(barRect)

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

// Create and append the corresponding accessibility element to accessibilityOrderedElements (see BarChartRenderer)
if let chart = dataProvider as? BarChartView
{
let element = createAccessibleElement(withIndex: j,
container: chart,
dataSet: dataSet,
dataSetIndex: index,
stackSize: stackSize)
{ (element) in
element.accessibilityFrame = barRect
}

accessibilityOrderedElements[j/stackSize].append(element)
context.addPath(path.cgPath)
context.strokePath()
}
}

Expand Down Expand Up @@ -629,4 +656,31 @@ open class HorizontalBarChartRenderer: BarChartRenderer
{
high.setDraw(x: barRect.midY, y: barRect.origin.x + barRect.size.width)
}

override internal func createBarPath(for rect: CGRect, roundedCorners: UIRectCorner, cornerRadius: CGFloat) -> UIBezierPath {

let cornerRadius = rect.height / 2.0

let path = UIBezierPath(roundedRect: rect,
byRoundingCorners: roundedCorners,
cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))

return path
}

private func findMostLeftRectInBar(barRects: [CGRect], firstIndexInBar: Int, lastIndexInBar: Int) -> CGRect {
var leftRectInBar = barRects[firstIndexInBar]
if barRects[lastIndexInBar].origin.x < leftRectInBar.origin.x {
leftRectInBar = barRects[lastIndexInBar]
}

var width: CGFloat = 0
for index in firstIndexInBar...lastIndexInBar {
width += barRects[index].width
}

leftRectInBar.size.width = width

return leftRectInBar
}
}

0 comments on commit 3096d61

Please sign in to comment.