From 54351442868f65dc3846e95f5b11141c8dee8e81 Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Tue, 25 Feb 2020 15:34:43 +0100 Subject: [PATCH 1/9] Update the renderers with dedicated atomic rendering methods in order to make rendering overrides easier to implement. --- .../Charts/Renderers/BarChartRenderer.swift | 110 ++++++++++++------ .../Renderers/BubbleChartRenderer.swift | 40 ++++++- Source/Charts/Renderers/LegendRenderer.swift | 101 +++++++++++++--- .../Charts/Renderers/LineChartRenderer.swift | 52 +++++---- .../Charts/Renderers/LineRadarRenderer.swift | 1 - .../Charts/Renderers/PieChartRenderer.swift | 61 +++++++--- 6 files changed, 270 insertions(+), 95 deletions(-) diff --git a/Source/Charts/Renderers/BarChartRenderer.swift b/Source/Charts/Renderers/BarChartRenderer.swift index c2ff0b108b..d819b2a7e9 100644 --- a/Source/Charts/Renderers/BarChartRenderer.swift +++ b/Source/Charts/Renderers/BarChartRenderer.swift @@ -322,7 +322,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer } private var _barShadowRectBuffer: CGRect = CGRect() - + @objc open func drawDataSet(context: CGContext, dataSet: IBarChartDataSet, index: Int) { guard let dataProvider = dataProvider else { return } @@ -334,9 +334,9 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer let borderWidth = dataSet.barBorderWidth let borderColor = dataSet.barBorderColor - let drawBorder = borderWidth > 0.0 context.saveGState() + defer { context.restoreGState() } // draw the bar shadow before the values if dataProvider.isDrawBarShadowEnabled @@ -371,8 +371,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer _barShadowRectBuffer.origin.y = viewPortHandler.contentTop _barShadowRectBuffer.size.height = viewPortHandler.contentHeight - context.setFillColor(dataSet.barShadowColor.cgColor) - context.fill(_barShadowRectBuffer) + renderShadow(with: dataSet.barShadowColor, for: _barShadowRectBuffer, in: context, dataSet: dataSet) } } @@ -395,17 +394,11 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer break } - context.setFillColor(dataSet.barShadowColor.cgColor) - context.fill(barRect) + renderShadow(with: dataSet.barShadowColor, for: barRect, in: context, dataSet: dataSet) } } let isSingleColor = dataSet.colors.count == 1 - - if isSingleColor - { - context.setFillColor(dataSet.color(atIndex: 0).cgColor) - } // In case the chart is stacked, we need to accomodate individual bars within accessibilityOrdereredElements let isStacked = dataSet.isStacked @@ -425,20 +418,16 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer 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) + renderFill(with: dataSet.color(atIndex: isSingleColor ? 0 : j), + for: barRect, + in: context, + dataSet: dataSet) - if drawBorder - { - context.setStrokeColor(borderColor.cgColor) - context.setLineWidth(borderWidth) - context.stroke(barRect) - } + renderBorder(with: borderColor, + width: borderWidth, + for: barRect, + in: context, + dataSet: dataSet) // Create and append the corresponding accessibility element to accessibilityOrderedElements if let chart = dataProvider as? BarChartView @@ -455,8 +444,6 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer accessibilityOrderedElements[j/stackSize].append(element) } } - - context.restoreGState() } open func prepareBarHighlight( @@ -777,10 +764,6 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer } let trans = dataProvider.getTransformer(forAxis: set.axisDependency) - - context.setFillColor(set.highlightColor.cgColor) - context.setAlpha(set.highlightAlpha) - let isStack = high.stackIndex >= 0 && e.isStacked let y1: Double @@ -808,10 +791,8 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer } prepareBarHighlight(x: e.x, y1: y1, y2: y2, barWidthHalf: barData.barWidth / 2.0, trans: trans, rect: &barRect) - setHighlightDrawPos(highlight: high, barRect: barRect) - - context.fill(barRect) + render(highlight: high, with: barRect, in: context, dataSet: set) } } @@ -901,4 +882,67 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer return element } + + // MARK: - Rendering override points - + + /// Render the fill of a (stacked) bar. + /// + /// - Parameters: + /// - color: the color to fill the bar with. + /// - rect: the rectangle of the (stacked) bar. + /// - context: the drawing context. + /// - dataset: the dataset that is rendered. + @objc open func renderFill(with color: UIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + context.saveGState() + context.setFillColor(color.cgColor) + context.fill(rect) + context.restoreGState() + } + + /// Render the border of a (stacked) bar. + /// + /// - Parameters: + /// - color: the border color. + /// - lineWidth: the line width width of the border. + /// - rect: the rectangle of the (stacked) bar. + /// - context: the drawing context. + /// - dataset: the dataset that is being rendered. + @objc open func renderBorder(with color: UIColor, width lineWidth: CGFloat, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + guard lineWidth > 0 else { return } + + context.saveGState() + context.setStrokeColor(color.cgColor) + context.setLineWidth(lineWidth) + context.stroke(rect) + context.restoreGState() + } + + /// Render the (stacked) highlight. + /// + /// - Parameters: + /// - highlight: the highlight to render. + /// - rect: the rectangle of the (stacked) bar. + /// - context: the drawing context. + /// - dataset: the dataset that is being rendered. + @objc open func render(highlight: Highlight, with rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + context.saveGState() + context.setFillColor(dataSet.highlightColor.cgColor) + context.setAlpha(dataSet.highlightAlpha) + context.fill(rect) + context.restoreGState() + } + + /// Render the (stacked) shadow. + /// + /// - Parameters: + /// - color: the shadow color. + /// - rect: the rectangle of the (stacked) bar. + /// - context: the drawing context. + /// - dataset: the dataset that is being rendered. + @objc open func renderShadow(with color: UIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + context.saveGState() + context.setFillColor(color.cgColor) + context.fill(rect) + context.restoreGState() + } } diff --git a/Source/Charts/Renderers/BubbleChartRenderer.swift b/Source/Charts/Renderers/BubbleChartRenderer.swift index 1a089e9f3e..82f093ef8c 100644 --- a/Source/Charts/Renderers/BubbleChartRenderer.swift +++ b/Source/Charts/Renderers/BubbleChartRenderer.swift @@ -128,8 +128,7 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer height: shapeSize ) - context.setFillColor(color.cgColor) - context.fillEllipse(in: rect) + renderFill(with: color, for: rect, in: context, dataSet: dataSet, dataEntry: entry) // Create and append the corresponding accessibility element to accessibilityOrderedElements if let chart = dataProvider as? BubbleChartView @@ -304,9 +303,13 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer width: shapeSize, height: shapeSize) - context.setLineWidth(dataSet.highlightCircleWidth) - context.setStrokeColor(color.cgColor) - context.strokeEllipse(in: rect) + render(highlight: high, + with: color, + for: rect, + lineWidth: dataSet.highlightCircleWidth, + in: context, + dataSet: dataSet, + entry: entry) high.setDraw(x: _pointBuffer.x, y: _pointBuffer.y) } @@ -356,4 +359,31 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer return element } + + // MARK: - Rendering override points - + + @objc open func renderFill(with color: UIColor, + for rect: CGRect, + in context: CGContext, + dataSet: IBubbleChartDataSet, + dataEntry: BubbleChartDataEntry) { + context.saveGState() + context.setFillColor(color.cgColor) + context.fillEllipse(in: rect) + context.restoreGState() + } + + @objc open func render(highlight: Highlight, + with color: UIColor, + for rect: CGRect, + lineWidth: CGFloat, + in context: CGContext, + dataSet: IBubbleChartDataSet, + entry: BubbleChartDataEntry) { + context.saveGState() + context.setLineWidth(lineWidth) + context.setStrokeColor(color.cgColor) + context.strokeEllipse(in: rect) + context.restoreGState() + } } diff --git a/Source/Charts/Renderers/LegendRenderer.swift b/Source/Charts/Renderers/LegendRenderer.swift index b9514b4fcf..5253ad3c55 100755 --- a/Source/Charts/Renderers/LegendRenderer.swift +++ b/Source/Charts/Renderers/LegendRenderer.swift @@ -535,38 +535,42 @@ open class LegendRenderer: Renderer case .default: fallthrough case .circle: - context.setFillColor(formColor.cgColor) - context.fillEllipse(in: CGRect(x: x, y: y - formSize / 2.0, width: formSize, height: formSize)) + renderCircle(with: formColor, + for: CGRect(x: x, y: y - formSize / 2.0, width: formSize, height: formSize), + in: context, + entry: entry, + legend: legend) case .square: - context.setFillColor(formColor.cgColor) - context.fill(CGRect(x: x, y: y - formSize / 2.0, width: formSize, height: formSize)) + renderSquare(with: formColor, + for: CGRect(x: x, y: y - formSize / 2.0, width: formSize, height: formSize), + in: context, + entry: entry, + legend: legend) case .line: let formLineWidth = entry.formLineWidth.isNaN ? legend.formLineWidth : entry.formLineWidth let formLineDashPhase = entry.formLineDashPhase.isNaN ? legend.formLineDashPhase : entry.formLineDashPhase let formLineDashLengths = entry.formLineDashLengths == nil ? legend.formLineDashLengths : entry.formLineDashLengths - - context.setLineWidth(formLineWidth) - - if formLineDashLengths != nil && formLineDashLengths!.count > 0 - { - context.setLineDash(phase: formLineDashPhase, lengths: formLineDashLengths!) - } - else - { - context.setLineDash(phase: 0.0, lengths: []) - } - - context.setStrokeColor(formColor.cgColor) - + _formLineSegmentsBuffer[0].x = x _formLineSegmentsBuffer[0].y = y _formLineSegmentsBuffer[1].x = x + formSize _formLineSegmentsBuffer[1].y = y - context.strokeLineSegments(between: _formLineSegmentsBuffer) + + let dashLengths = formLineDashLengths ?? [] + let dashPhase = !dashLengths.isEmpty ? formLineDashPhase : 0 + + renderLine(with: formColor, + lineWidth: formLineWidth, + dashPhase: dashPhase, + dashLengths: dashLengths, + between: _formLineSegmentsBuffer, + in: context, + entry: entry, + legend: legend) } } @@ -575,4 +579,63 @@ open class LegendRenderer: Renderer { ChartUtils.drawText(context: context, text: label, point: CGPoint(x: x, y: y), align: .left, attributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: textColor]) } + + // MARK: - Rendering override points - + + /// Render a round legend. + /// + /// - Parameters: + /// - color: the fill color of the legend. + /// - rect: the rectangle for which to render the circle. + /// - context: the drawing context. + /// - entry: the legend entry. + /// - legend: the legend. + @objc open func renderCircle(with color: UIColor, for rect: CGRect, in context: CGContext, entry: LegendEntry, legend: Legend) { + context.saveGState() + context.setFillColor(color.cgColor) + context.fillEllipse(in: rect) + context.restoreGState() + } + + /// Render a square legend + /// + /// - Parameters: + /// - color: the fill color of the legend. + /// - rect: the rectangle of the square to render. + /// - context: the drawing context. + /// - entry: the legend entry. + /// - legend: the legend. + @objc open func renderSquare(with color: UIColor, for rect: CGRect, in context: CGContext, entry: LegendEntry, legend: Legend) { + context.saveGState() + context.setFillColor(color.cgColor) + context.fill(rect) + context.restoreGState() + } + + /// Render a line legend. + /// + /// - Parameters: + /// - color: the line color. + /// - lineWidth: the width of the line. + /// - dashPhase: the dash phase pattern for dashed lines. + /// - dashLengths: the pattern for dashed lines. + /// - between: stroke a sequence of line segments. + /// - context: the drawing context. + /// - entry: the legend entry. + /// - legend: the legend. + @objc open func renderLine(with color: UIColor, + lineWidth: CGFloat, + dashPhase: CGFloat, + dashLengths: [CGFloat], + between: [CGPoint], + in context: CGContext, + entry: LegendEntry, + legend: Legend) { + context.saveGState() + context.setLineWidth(lineWidth) + context.setLineDash(phase: dashPhase, lengths: dashLengths) + context.setStrokeColor(color.cgColor) + context.strokeLineSegments(between: _formLineSegmentsBuffer) + context.restoreGState() + } } diff --git a/Source/Charts/Renderers/LineChartRenderer.swift b/Source/Charts/Renderers/LineChartRenderer.swift index ce3a25915e..45597f6352 100644 --- a/Source/Charts/Renderers/LineChartRenderer.swift +++ b/Source/Charts/Renderers/LineChartRenderer.swift @@ -162,6 +162,7 @@ open class LineChartRenderer: LineRadarRenderer } context.saveGState() + defer { context.restoreGState() } if dataSet.isDrawFilledEnabled { @@ -171,12 +172,7 @@ open class LineChartRenderer: LineRadarRenderer drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, bounds: _xBounds) } - context.beginPath() - context.addPath(cubicPath) - context.setStrokeColor(drawingColor.cgColor) - context.strokePath() - - context.restoreGState() + renderLine(with: cubicPath, color: drawingColor, in: context, dataSet: dataSet) } @objc open func drawHorizontalBezier(context: CGContext, dataSet: ILineChartDataSet) @@ -229,6 +225,7 @@ open class LineChartRenderer: LineRadarRenderer } context.saveGState() + defer { context.restoreGState() } if dataSet.isDrawFilledEnabled { @@ -237,13 +234,8 @@ open class LineChartRenderer: LineRadarRenderer drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, bounds: _xBounds) } - - context.beginPath() - context.addPath(cubicPath) - context.setStrokeColor(drawingColor.cgColor) - context.strokePath() - - context.restoreGState() + + renderLine(with: cubicPath, color: drawingColor, in: context, dataSet: dataSet) } open func drawCubicFill( @@ -308,12 +300,13 @@ open class LineChartRenderer: LineRadarRenderer } context.saveGState() + defer { context.restoreGState() } - if _lineSegments.count != pointsPerEntryPair - { - // Allocate once in correct size - _lineSegments = [CGPoint](repeating: CGPoint(), count: pointsPerEntryPair) - } + if _lineSegments.count != pointsPerEntryPair + { + // Allocate once in correct size + _lineSegments = [CGPoint](repeating: CGPoint(), count: pointsPerEntryPair) + } for j in _xBounds.dropLast() { @@ -374,11 +367,8 @@ open class LineChartRenderer: LineRadarRenderer } // get the color that is set for this line-segment - context.setStrokeColor(dataSet.color(atIndex: j).cgColor) - context.strokeLineSegments(between: _lineSegments) + renderLine(between: _lineSegments, color: dataSet.color(atIndex: j), in: context, dataSet: dataSet) } - - context.restoreGState() } open func drawLinearFill(context: CGContext, dataSet: ILineChartDataSet, trans: Transformer, bounds: XBounds) @@ -789,4 +779,22 @@ open class LineChartRenderer: LineRadarRenderer return element } + + // MARK: - Rendering override points - + + @objc open func renderLine(with path: CGPath, color: UIColor, in context: CGContext, dataSet: ILineChartDataSet) { + context.saveGState() + context.beginPath() + context.addPath(path) + context.setStrokeColor(color.cgColor) + context.strokePath() + context.restoreGState() + } + + @objc open func renderLine(between points: [CGPoint], color: UIColor, in context: CGContext, dataSet: ILineChartDataSet) { + context.saveGState() + context.setStrokeColor(color.cgColor) + context.strokeLineSegments(between: points) + context.restoreGState() + } } diff --git a/Source/Charts/Renderers/LineRadarRenderer.swift b/Source/Charts/Renderers/LineRadarRenderer.swift index 6b02825053..ebed72926c 100644 --- a/Source/Charts/Renderers/LineRadarRenderer.swift +++ b/Source/Charts/Renderers/LineRadarRenderer.swift @@ -23,7 +23,6 @@ open class LineRadarRenderer: LineScatterCandleRadarRenderer /// Draws the provided path in filled mode with the provided drawable. @objc open func drawFilledPath(context: CGContext, path: CGPath, fill: Fill, fillAlpha: CGFloat) { - context.saveGState() context.beginPath() context.addPath(path) diff --git a/Source/Charts/Renderers/PieChartRenderer.swift b/Source/Charts/Renderers/PieChartRenderer.swift index 0985a3da8d..077e558b32 100644 --- a/Source/Charts/Renderers/PieChartRenderer.swift +++ b/Source/Charts/Renderers/PieChartRenderer.swift @@ -139,6 +139,7 @@ open class PieChartRenderer: DataRenderer let sliceSpace = visibleAngleCount <= 1 ? 0.0 : getSliceSpace(dataSet: dataSet) context.saveGState() + defer { context.restoreGState() } // Make the chart header the first element in the accessible elements array // We can do this in drawDataSet, since we know PieChartView can have only 1 dataSet @@ -181,9 +182,6 @@ open class PieChartRenderer: DataRenderer } let accountForSliceSpacing = sliceSpace > 0.0 && sliceAngle <= 180.0 - - context.setFillColor(dataSet.color(atIndex: j).cgColor) - let sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.0 : sliceSpace / radius.DEG2RAD @@ -274,9 +272,7 @@ open class PieChartRenderer: DataRenderer path.closeSubpath() - context.beginPath() - context.addPath(path) - context.fillPath(using: .evenOdd) + renderFill(with: dataSet.color(atIndex: j), for: path, in: context, dataSet: dataSet, entry: e) let axElement = createAccessibleElement(withIndex: j, container: chart, @@ -290,8 +286,6 @@ open class PieChartRenderer: DataRenderer // Post this notification to let VoiceOver account for the redrawn frames accessibilityPostLayoutChangedNotification() - - context.restoreGState() } open override func drawValues(context: CGContext) @@ -715,6 +709,7 @@ open class PieChartRenderer: DataRenderer else { return } context.saveGState() + defer { context.restoreGState() } let phaseX = animator.phaseX let phaseY = animator.phaseY @@ -775,8 +770,6 @@ open class PieChartRenderer: DataRenderer let accountForSliceSpacing = sliceSpace > 0.0 && sliceAngle <= 180.0 - context.setFillColor(set.highlightColor?.cgColor ?? set.color(atIndex: index).cgColor) - let sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.0 : sliceSpace / radius.DEG2RAD @@ -875,9 +868,12 @@ open class PieChartRenderer: DataRenderer path.closeSubpath() - context.beginPath() - context.addPath(path) - context.fillPath(using: .evenOdd) + render(highlight: indices[i], + with: set.highlightColor ?? set.color(atIndex: index), + for: path, + in: context, + dataSet: set, + entry: set.entryForIndex(index)) let axElement = createAccessibleElement(withIndex: index, container: chart, @@ -895,8 +891,6 @@ open class PieChartRenderer: DataRenderer if !accessibleChartElements.isEmpty { accessibleChartElements.insert(contentsOf: highlightedAccessibleElements, at: 1) } - - context.restoreGState() } /// Creates an NSUIAccessibilityElement representing a slice of the PieChart. @@ -940,4 +934,41 @@ open class PieChartRenderer: DataRenderer return element } + + // MARK: - Rendering override points - + + /// Render the fill of a pie segment. + /// + /// - Parameters: + /// - color: the fill color. + /// - path: the path of pie segment. + /// - context: the drawing context. + /// - dataSet: the dataset that is being rendered. + /// - entry: the data entry that is being filled. + @objc open func renderFill(with color: UIColor, for path: CGPath, in context: CGContext, dataSet: IPieChartDataSet, entry: ChartDataEntry) { + context.saveGState() + context.beginPath() + context.addPath(path) + context.setFillColor(color.cgColor) + context.fillPath(using: .evenOdd) + context.restoreGState() + } + + /// Render the higlighted pie segment. + /// + /// - Parameters: + /// - highlight: the highlight to render. + /// - color: the highlight color. + /// - path: the path of pie segment. + /// - context: the drawing context. + /// - dataSet: the dataset that is being rendered. + /// - entry: the data entry that is being highlighted. + @objc open func render(highlight: Highlight, with color: UIColor, for path: CGPath, in context: CGContext, dataSet: IPieChartDataSet, entry: ChartDataEntry?) { + context.saveGState() + context.beginPath() + context.addPath(path) + context.setFillColor(color.cgColor) + context.fillPath(using: .evenOdd) + context.restoreGState() + } } From a4e6e8ef6c20c1f63984f6c25928e0fcaee2e8b7 Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Tue, 25 Feb 2020 16:53:33 +0100 Subject: [PATCH 2/9] Changed `UIColor` in platform independant `NSUIColor`. --- Source/Charts/Renderers/BarChartRenderer.swift | 6 +++--- Source/Charts/Renderers/BubbleChartRenderer.swift | 4 ++-- Source/Charts/Renderers/LegendRenderer.swift | 6 +++--- Source/Charts/Renderers/LineChartRenderer.swift | 4 ++-- Source/Charts/Renderers/PieChartRenderer.swift | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Charts/Renderers/BarChartRenderer.swift b/Source/Charts/Renderers/BarChartRenderer.swift index d819b2a7e9..53948cefbe 100644 --- a/Source/Charts/Renderers/BarChartRenderer.swift +++ b/Source/Charts/Renderers/BarChartRenderer.swift @@ -892,7 +892,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer /// - rect: the rectangle of the (stacked) bar. /// - context: the drawing context. /// - dataset: the dataset that is rendered. - @objc open func renderFill(with color: UIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + @objc open func renderFill(with color: NSUIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { context.saveGState() context.setFillColor(color.cgColor) context.fill(rect) @@ -907,7 +907,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer /// - rect: the rectangle of the (stacked) bar. /// - context: the drawing context. /// - dataset: the dataset that is being rendered. - @objc open func renderBorder(with color: UIColor, width lineWidth: CGFloat, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + @objc open func renderBorder(with color: NSUIColor, width lineWidth: CGFloat, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { guard lineWidth > 0 else { return } context.saveGState() @@ -939,7 +939,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer /// - rect: the rectangle of the (stacked) bar. /// - context: the drawing context. /// - dataset: the dataset that is being rendered. - @objc open func renderShadow(with color: UIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + @objc open func renderShadow(with color: NSUIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { context.saveGState() context.setFillColor(color.cgColor) context.fill(rect) diff --git a/Source/Charts/Renderers/BubbleChartRenderer.swift b/Source/Charts/Renderers/BubbleChartRenderer.swift index 82f093ef8c..49f3c993a9 100644 --- a/Source/Charts/Renderers/BubbleChartRenderer.swift +++ b/Source/Charts/Renderers/BubbleChartRenderer.swift @@ -362,7 +362,7 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer // MARK: - Rendering override points - - @objc open func renderFill(with color: UIColor, + @objc open func renderFill(with color: NSUIColor, for rect: CGRect, in context: CGContext, dataSet: IBubbleChartDataSet, @@ -374,7 +374,7 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer } @objc open func render(highlight: Highlight, - with color: UIColor, + with color: NSUIColor, for rect: CGRect, lineWidth: CGFloat, in context: CGContext, diff --git a/Source/Charts/Renderers/LegendRenderer.swift b/Source/Charts/Renderers/LegendRenderer.swift index 5253ad3c55..f7dadd8028 100755 --- a/Source/Charts/Renderers/LegendRenderer.swift +++ b/Source/Charts/Renderers/LegendRenderer.swift @@ -590,7 +590,7 @@ open class LegendRenderer: Renderer /// - context: the drawing context. /// - entry: the legend entry. /// - legend: the legend. - @objc open func renderCircle(with color: UIColor, for rect: CGRect, in context: CGContext, entry: LegendEntry, legend: Legend) { + @objc open func renderCircle(with color: NSUIColor, for rect: CGRect, in context: CGContext, entry: LegendEntry, legend: Legend) { context.saveGState() context.setFillColor(color.cgColor) context.fillEllipse(in: rect) @@ -605,7 +605,7 @@ open class LegendRenderer: Renderer /// - context: the drawing context. /// - entry: the legend entry. /// - legend: the legend. - @objc open func renderSquare(with color: UIColor, for rect: CGRect, in context: CGContext, entry: LegendEntry, legend: Legend) { + @objc open func renderSquare(with color: NSUIColor, for rect: CGRect, in context: CGContext, entry: LegendEntry, legend: Legend) { context.saveGState() context.setFillColor(color.cgColor) context.fill(rect) @@ -623,7 +623,7 @@ open class LegendRenderer: Renderer /// - context: the drawing context. /// - entry: the legend entry. /// - legend: the legend. - @objc open func renderLine(with color: UIColor, + @objc open func renderLine(with color: NSUIColor, lineWidth: CGFloat, dashPhase: CGFloat, dashLengths: [CGFloat], diff --git a/Source/Charts/Renderers/LineChartRenderer.swift b/Source/Charts/Renderers/LineChartRenderer.swift index 45597f6352..86a20ec113 100644 --- a/Source/Charts/Renderers/LineChartRenderer.swift +++ b/Source/Charts/Renderers/LineChartRenderer.swift @@ -782,7 +782,7 @@ open class LineChartRenderer: LineRadarRenderer // MARK: - Rendering override points - - @objc open func renderLine(with path: CGPath, color: UIColor, in context: CGContext, dataSet: ILineChartDataSet) { + @objc open func renderLine(with path: CGPath, color: NSUIColor, in context: CGContext, dataSet: ILineChartDataSet) { context.saveGState() context.beginPath() context.addPath(path) @@ -791,7 +791,7 @@ open class LineChartRenderer: LineRadarRenderer context.restoreGState() } - @objc open func renderLine(between points: [CGPoint], color: UIColor, in context: CGContext, dataSet: ILineChartDataSet) { + @objc open func renderLine(between points: [CGPoint], color: NSUIColor, in context: CGContext, dataSet: ILineChartDataSet) { context.saveGState() context.setStrokeColor(color.cgColor) context.strokeLineSegments(between: points) diff --git a/Source/Charts/Renderers/PieChartRenderer.swift b/Source/Charts/Renderers/PieChartRenderer.swift index 077e558b32..daf12e3336 100644 --- a/Source/Charts/Renderers/PieChartRenderer.swift +++ b/Source/Charts/Renderers/PieChartRenderer.swift @@ -945,7 +945,7 @@ open class PieChartRenderer: DataRenderer /// - context: the drawing context. /// - dataSet: the dataset that is being rendered. /// - entry: the data entry that is being filled. - @objc open func renderFill(with color: UIColor, for path: CGPath, in context: CGContext, dataSet: IPieChartDataSet, entry: ChartDataEntry) { + @objc open func renderFill(with color: NSUIColor, for path: CGPath, in context: CGContext, dataSet: IPieChartDataSet, entry: ChartDataEntry) { context.saveGState() context.beginPath() context.addPath(path) @@ -963,7 +963,7 @@ open class PieChartRenderer: DataRenderer /// - context: the drawing context. /// - dataSet: the dataset that is being rendered. /// - entry: the data entry that is being highlighted. - @objc open func render(highlight: Highlight, with color: UIColor, for path: CGPath, in context: CGContext, dataSet: IPieChartDataSet, entry: ChartDataEntry?) { + @objc open func render(highlight: Highlight, with color: NSUIColor, for path: CGPath, in context: CGContext, dataSet: IPieChartDataSet, entry: ChartDataEntry?) { context.saveGState() context.beginPath() context.addPath(path) From d3e82c360aa67cc0f90f37823c2dacef3730a082 Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Thu, 27 Feb 2020 12:25:22 +0100 Subject: [PATCH 3/9] Added the chart data entry to the bar chart rendering overrides. --- .../Charts/Renderers/BarChartRenderer.swift | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/Source/Charts/Renderers/BarChartRenderer.swift b/Source/Charts/Renderers/BarChartRenderer.swift index 53948cefbe..92597468da 100644 --- a/Source/Charts/Renderers/BarChartRenderer.swift +++ b/Source/Charts/Renderers/BarChartRenderer.swift @@ -371,7 +371,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer _barShadowRectBuffer.origin.y = viewPortHandler.contentTop _barShadowRectBuffer.size.height = viewPortHandler.contentHeight - renderShadow(with: dataSet.barShadowColor, for: _barShadowRectBuffer, in: context, dataSet: dataSet) + renderShadow(with: dataSet.barShadowColor, for: _barShadowRectBuffer, in: context, dataSet: dataSet, entry: e) } } @@ -394,7 +394,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer break } - renderShadow(with: dataSet.barShadowColor, for: barRect, in: context, dataSet: dataSet) + renderShadow(with: dataSet.barShadowColor, for: barRect, in: context, dataSet: dataSet, entry: dataSet.entryForIndex(j) as? BarChartDataEntry) } } @@ -421,13 +421,15 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer renderFill(with: dataSet.color(atIndex: isSingleColor ? 0 : j), for: barRect, in: context, - dataSet: dataSet) + dataSet: dataSet, + entry: dataSet.entryForIndex(j) as? BarChartDataEntry) renderBorder(with: borderColor, width: borderWidth, for: barRect, in: context, - dataSet: dataSet) + dataSet: dataSet, + entry: dataSet.entryForIndex(j) as? BarChartDataEntry) // Create and append the corresponding accessibility element to accessibilityOrderedElements if let chart = dataProvider as? BarChartView @@ -792,7 +794,7 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer prepareBarHighlight(x: e.x, y1: y1, y2: y2, barWidthHalf: barData.barWidth / 2.0, trans: trans, rect: &barRect) setHighlightDrawPos(highlight: high, barRect: barRect) - render(highlight: high, with: barRect, in: context, dataSet: set) + render(highlight: high, with: barRect, in: context, dataSet: set, entry: e) } } @@ -891,8 +893,9 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer /// - color: the color to fill the bar with. /// - rect: the rectangle of the (stacked) bar. /// - context: the drawing context. - /// - dataset: the dataset that is rendered. - @objc open func renderFill(with color: NSUIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + /// - dataset: the dataset that is being rendered. + /// - entry: the entry that is being rendered. + @objc open func renderFill(with color: NSUIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet, entry: BarChartDataEntry?) { context.saveGState() context.setFillColor(color.cgColor) context.fill(rect) @@ -907,7 +910,13 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer /// - rect: the rectangle of the (stacked) bar. /// - context: the drawing context. /// - dataset: the dataset that is being rendered. - @objc open func renderBorder(with color: NSUIColor, width lineWidth: CGFloat, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + /// - entry: the entry that is being rendered. + @objc open func renderBorder(with color: NSUIColor, + width lineWidth: CGFloat, + for rect: CGRect, + in context: CGContext, + dataSet: IBarChartDataSet, + entry: BarChartDataEntry?) { guard lineWidth > 0 else { return } context.saveGState() @@ -924,7 +933,8 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer /// - rect: the rectangle of the (stacked) bar. /// - context: the drawing context. /// - dataset: the dataset that is being rendered. - @objc open func render(highlight: Highlight, with rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + /// - entry: the entry that is being rendered. + @objc open func render(highlight: Highlight, with rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet, entry: BarChartDataEntry) { context.saveGState() context.setFillColor(dataSet.highlightColor.cgColor) context.setAlpha(dataSet.highlightAlpha) @@ -939,7 +949,8 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer /// - rect: the rectangle of the (stacked) bar. /// - context: the drawing context. /// - dataset: the dataset that is being rendered. - @objc open func renderShadow(with color: NSUIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet) { + /// - entry: the entry that is being rendered. + @objc open func renderShadow(with color: NSUIColor, for rect: CGRect, in context: CGContext, dataSet: IBarChartDataSet, entry: BarChartDataEntry?) { context.saveGState() context.setFillColor(color.cgColor) context.fill(rect) From 0ff8d4e97447ae87551b88e29ad2317f4d75cfb6 Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Tue, 15 Dec 2020 14:02:41 +0100 Subject: [PATCH 4/9] Added missing in-line documentation. --- .../Renderers/BubbleChartRenderer.swift | 19 +++++++++++++++ .../Charts/Renderers/LineChartRenderer.swift | 23 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/Source/Charts/Renderers/BubbleChartRenderer.swift b/Source/Charts/Renderers/BubbleChartRenderer.swift index fa410cdaf9..82943e051f 100644 --- a/Source/Charts/Renderers/BubbleChartRenderer.swift +++ b/Source/Charts/Renderers/BubbleChartRenderer.swift @@ -362,6 +362,14 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer // MARK: - Rendering override points - + /// Render bubble fill. + /// + /// - Parameters: + /// - color: the fill color of the bubble. + /// - rect: the rectangle for which to render the bubble. + /// - context: the drawing context. + /// - dataSet: the dataset that is being rendered. + /// - dataEntry: the entry that is being rendered. @objc open func renderFill(with color: NSUIColor, for rect: CGRect, in context: CGContext, @@ -373,6 +381,17 @@ open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer context.restoreGState() } + + /// Render a highlighted bubble. + /// + /// - Parameters: + /// - highlight: the hightligh to render. + /// - color: the color of the highlighted bubble. + /// - rect: the rectangle for which to render the bubble. + /// - lineWidth: the width of the border. + /// - context: the drawing context. + /// - dataSet: the dataset dat is being rendered. + /// - entry: the entry that is being rendered. @objc open func render(highlight: Highlight, with color: NSUIColor, for rect: CGRect, diff --git a/Source/Charts/Renderers/LineChartRenderer.swift b/Source/Charts/Renderers/LineChartRenderer.swift index b3b9fe2ed3..b0b4655c24 100644 --- a/Source/Charts/Renderers/LineChartRenderer.swift +++ b/Source/Charts/Renderers/LineChartRenderer.swift @@ -892,6 +892,13 @@ open class LineChartRenderer: LineRadarRenderer // MARK: - Rendering override points - + /// Render a line. + /// + /// - Parameters: + /// - path: the path that should be rendered. + /// - color: the color of the line. + /// - context: the drawing context. + /// - dataSet: the dataset that is being rendered. @objc open func renderLine(with path: CGPath, color: NSUIColor, in context: CGContext, dataSet: LineChartDataSetProtocol) { context.saveGState() context.beginPath() @@ -901,6 +908,15 @@ open class LineChartRenderer: LineRadarRenderer context.restoreGState() } + /// Render a line with a gradient. + /// + /// - Parameters: + /// - path: the path that should be rendered. + /// - gradient: the gradient. + /// - startPoint: the gradient's start point. + /// - endPoint: the gradient's end point. + /// - context: the drawing context. + /// - dataSet: the dataset that is being rendered. @objc open func renderGradientLine(with path: CGPath, linearGradient gradient: CGGradient, startingAt startPoint: CGPoint, @@ -916,6 +932,13 @@ open class LineChartRenderer: LineRadarRenderer context.restoreGState() } + /// Render a line between points. + /// + /// - Parameters: + /// - points: the collection of points to draw the line between. + /// - color: the color of the line. + /// - context: the drawing context. + /// - dataSet: the dataset that is being rendered. @objc open func renderLine(between points: [CGPoint], color: NSUIColor, in context: CGContext, dataSet: LineChartDataSetProtocol) { context.saveGState() context.setStrokeColor(color.cgColor) From a517e146fa3ef95817e1cc18c00c6da2d2124ca8 Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Tue, 15 Dec 2020 14:09:55 +0100 Subject: [PATCH 5/9] Added a fix for #3701 --- Source/Charts/Components/AxisBase.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Charts/Components/AxisBase.swift b/Source/Charts/Components/AxisBase.swift index 2544f90d77..064954cdd1 100644 --- a/Source/Charts/Components/AxisBase.swift +++ b/Source/Charts/Components/AxisBase.swift @@ -134,14 +134,17 @@ open class AxisBase: ComponentBase @objc open func getLongestLabel() -> String { var longest = "" + var currentWidth: CGFloat = 0 for i in entries.indices { let text = getFormattedLabel(i) + let textWidth = text.size(withAttributes: [NSAttributedString.Key.font: labelFont]).width - if longest.count < text.count + if longest.count <= text.count, currentWidth < textWidth { longest = text + currentWidth = textWidth } } From 7e1f6a0a7853a06e818a52ebb3c80f055755e978 Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Tue, 15 Dec 2020 15:21:54 +0100 Subject: [PATCH 6/9] Revert "Added a fix for #3701" This reverts commit a517e146fa3ef95817e1cc18c00c6da2d2124ca8. --- Source/Charts/Components/AxisBase.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Source/Charts/Components/AxisBase.swift b/Source/Charts/Components/AxisBase.swift index 064954cdd1..2544f90d77 100644 --- a/Source/Charts/Components/AxisBase.swift +++ b/Source/Charts/Components/AxisBase.swift @@ -134,17 +134,14 @@ open class AxisBase: ComponentBase @objc open func getLongestLabel() -> String { var longest = "" - var currentWidth: CGFloat = 0 for i in entries.indices { let text = getFormattedLabel(i) - let textWidth = text.size(withAttributes: [NSAttributedString.Key.font: labelFont]).width - if longest.count <= text.count, currentWidth < textWidth + if longest.count < text.count { longest = text - currentWidth = textWidth } } From a5f094d4b8430e4703c4ab67508b8ef7c95f2d6c Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Tue, 15 Dec 2020 15:36:34 +0100 Subject: [PATCH 7/9] No need for drawBorder as that is already being handled by the renderBorder method. --- Source/Charts/Renderers/BarChartRenderer.swift | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Source/Charts/Renderers/BarChartRenderer.swift b/Source/Charts/Renderers/BarChartRenderer.swift index dacb2e48cf..a5513840ee 100644 --- a/Source/Charts/Renderers/BarChartRenderer.swift +++ b/Source/Charts/Renderers/BarChartRenderer.swift @@ -303,7 +303,6 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer let borderWidth = dataSet.barBorderWidth let borderColor = dataSet.barBorderColor - let drawBorder = borderWidth > 0.0 context.saveGState() defer { context.restoreGState() } @@ -376,14 +375,12 @@ open class BarChartRenderer: BarLineScatterCandleBubbleRenderer dataSet: dataSet, entry: dataSet.entryForIndex(j) as? BarChartDataEntry) - if drawBorder { - renderBorder(with: borderColor, - width: borderWidth, - for: barRect, - in: context, - dataSet: dataSet, - entry: dataSet.entryForIndex(j) as? BarChartDataEntry) - } + renderBorder(with: borderColor, + width: borderWidth, + for: barRect, + in: context, + dataSet: dataSet, + entry: dataSet.entryForIndex(j) as? BarChartDataEntry) // Create and append the corresponding accessibility element to accessibilityOrderedElements if let chart = dataProvider as? BarChartView From 271a5ee29dfbe3f740ea52fe97bd683109017757 Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Tue, 15 Dec 2020 17:07:36 +0100 Subject: [PATCH 8/9] Added rendering override methods for CandleStickChartRenderer. --- .../CandleStickChartViewController.swift | 6 +- .../Renderers/CandleStickChartRenderer.swift | 163 ++++++++++++------ 2 files changed, 113 insertions(+), 56 deletions(-) diff --git a/ChartsDemo-iOS/Swift/Demos/CandleStickChartViewController.swift b/ChartsDemo-iOS/Swift/Demos/CandleStickChartViewController.swift index 33cb7edc1d..c47b82b0a4 100644 --- a/ChartsDemo-iOS/Swift/Demos/CandleStickChartViewController.swift +++ b/ChartsDemo-iOS/Swift/Demos/CandleStickChartViewController.swift @@ -113,8 +113,10 @@ class CandleStickChartViewController: DemoBaseViewController { } chartView.notifyDataSetChanged() case .toggleShowCandleBar: - for set in chartView.data!.dataSets as! [CandleChartDataSet] { - set.showCandleBar = !set.showCandleBar + if let datasets = chartView.data?.dataSets as? [CandleChartDataSet] { + for set in datasets { + set.showCandleBar.toggle() + } } chartView.notifyDataSetChanged() default: diff --git a/Source/Charts/Renderers/CandleStickChartRenderer.swift b/Source/Charts/Renderers/CandleStickChartRenderer.swift index 5690fdb74a..6fa7e51353 100644 --- a/Source/Charts/Renderers/CandleStickChartRenderer.swift +++ b/Source/Charts/Renderers/CandleStickChartRenderer.swift @@ -66,8 +66,7 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) context.saveGState() - - context.setLineWidth(dataSet.shadowWidth) + defer { context.restoreGState() } for j in _xBounds { @@ -145,10 +144,7 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer { shadowColor = dataSet.shadowColor ?? dataSet.color(atIndex: j) } - - context.setStrokeColor(shadowColor.cgColor) - context.strokeLineSegments(between: _shadowPoints) - + // calculate the body _bodyRect.origin.x = CGFloat(xPos) - 0.5 + barSpace @@ -166,16 +162,13 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer let color = dataSet.decreasingColor ?? dataSet.color(atIndex: j) - if dataSet.isDecreasingFilled - { - context.setFillColor(color.cgColor) - context.fill(_bodyRect) - } - else - { - context.setStrokeColor(color.cgColor) - context.stroke(_bodyRect) - } + renderCandleStick(with: _bodyRect, + color: color, + filled: dataSet.isDecreasingFilled, + shadowBetween: _shadowPoints, + shadowColor: shadowColor, + in: context, + dataSet: dataSet) } else if open < close { @@ -183,23 +176,25 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer let color = dataSet.increasingColor ?? dataSet.color(atIndex: j) - if dataSet.isIncreasingFilled - { - context.setFillColor(color.cgColor) - context.fill(_bodyRect) - } - else - { - context.setStrokeColor(color.cgColor) - context.stroke(_bodyRect) - } + renderCandleStick(with: _bodyRect, + color: color, + filled: dataSet.isIncreasingFilled, + shadowBetween: _shadowPoints, + shadowColor: shadowColor, + in: context, + dataSet: dataSet) } else { let color = dataSet.neutralColor ?? dataSet.color(atIndex: j) - - context.setStrokeColor(color.cgColor) - context.stroke(_bodyRect) + + renderCandleStick(with: _bodyRect, + color: color, + filled: false, + shadowBetween: _shadowPoints, + shadowColor: shadowColor, + in: context, + dataSet: dataSet) } } else @@ -240,11 +235,8 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer { barColor = dataSet.neutralColor ?? dataSet.color(atIndex: j) } - - context.setStrokeColor(barColor.cgColor) - context.strokeLineSegments(between: _rangePoints) - context.strokeLineSegments(between: _openPoints) - context.strokeLineSegments(between: _closePoints) + + renderBar(with: barColor, rangePoints: _rangePoints, openPoints: _openPoints, closedPoints: _closePoints, in: context, dataSet: dataSet) } let axElement = createAccessibleElement(withIndex: j, @@ -261,8 +253,6 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer // Post this notification to let VoiceOver account for the redrawn frames accessibilityPostLayoutChangedNotification() - - context.restoreGState() } open override func drawValues(context: CGContext) @@ -358,6 +348,7 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer else { return } context.saveGState() + defer { context.restoreGState() } for high in indices { @@ -374,19 +365,6 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer } let trans = dataProvider.getTransformer(forAxis: set.axisDependency) - - context.setStrokeColor(set.highlightColor.cgColor) - context.setLineWidth(set.highlightLineWidth) - - if set.highlightLineDashLengths != nil - { - context.setLineDash(phase: set.highlightLineDashPhase, lengths: set.highlightLineDashLengths!) - } - else - { - context.setLineDash(phase: 0.0, lengths: []) - } - let lowValue = e.low * Double(animator.phaseY) let highValue = e.high * Double(animator.phaseY) let y = (lowValue + highValue) / 2.0 @@ -394,12 +372,8 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer let pt = trans.pixelForValues(x: e.x, y: y) high.setDraw(pt: pt) - - // draw the lines - drawHighlightLines(context: context, point: pt, set: set) + renderHighlight(high, at: pt, in: context, dataSet: set) } - - context.restoreGState() } private func createAccessibleElement(withIndex idx: Int, @@ -414,4 +388,85 @@ open class CandleStickChartRenderer: LineScatterCandleRadarRenderer return element } + + // MARK: - Rendering override points - + + /// Render a candle stick. + /// + /// - Parameters: + /// - rect: the rectangle of the body of the candlestick. + /// - color: the fill or stroke color of the body of the candlestick. + /// - shouldFill: whether the body should be filled or stroked. + /// - shadowPoints: the points that outline the shadow. + /// - shadowColor: the color of the shadow. + /// - context: the drawing context. + /// - dataSet: the dataset that is being rendered. + @objc open func renderCandleStick(with rect: CGRect, + color: NSUIColor, + filled: Bool, + shadowBetween shadowPoints: [CGPoint], + shadowColor: NSUIColor, + in context: CGContext, + dataSet: CandleChartDataSetProtocol) { + context.saveGState() + + // Render the shadow + context.setStrokeColor(shadowColor.cgColor) + context.setLineWidth(dataSet.shadowWidth) + context.strokeLineSegments(between: shadowPoints) + + // Render the body + if filled { + context.setFillColor(color.cgColor) + context.fill(rect) + } else { + context.setStrokeColor(color.cgColor) + context.stroke(rect) + } + + context.restoreGState() + } + + /// Render a candle stick bar. + /// + /// - Parameters: + /// - color: the color of the bar. + /// - rangePoints: the points that represent range. + /// - openPoints: open points. + /// - closedPoints: closed points. + /// - context: the drawing context + /// - dataSet: the dataset that is being rendered. + @objc open func renderBar(with color: NSUIColor, + rangePoints: [CGPoint], + openPoints: [CGPoint], + closedPoints: [CGPoint], + in context: CGContext, + dataSet: CandleChartDataSetProtocol) { + context.saveGState() + context.setStrokeColor(color.cgColor) + context.strokeLineSegments(between: rangePoints) + context.strokeLineSegments(between: openPoints) + context.strokeLineSegments(between: closedPoints) + context.restoreGState() + } + + /// Render highlight. + /// + /// - Parameters: + /// - highlight: the hightlight that is being rendered. + /// - point: where to render the highlight. + /// - context: the drawing context. + /// - dataSet: the dataset that is being rendered. + @objc func renderHighlight(_ highlight: Highlight, at point: CGPoint, in context: CGContext, dataSet: CandleChartDataSetProtocol) { + context.saveGState() + context.setStrokeColor(dataSet.highlightColor.cgColor) + context.setLineWidth(dataSet.highlightLineWidth) + + if let dashLengths = dataSet.highlightLineDashLengths { + context.setLineDash(phase: dataSet.highlightLineDashPhase, lengths: dashLengths) + } + + drawHighlightLines(context: context, point: point, set: dataSet) + context.restoreGState() + } } From f83591be4372d610750fc0bb954b42c40b6966a4 Mon Sep 17 00:00:00 2001 From: Jeroen Wesbeek Date: Tue, 15 Dec 2020 17:15:23 +0100 Subject: [PATCH 9/9] Fixed some crash-prone force unwraps. --- .../Demos/CandleStickChartViewController.swift | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ChartsDemo-iOS/Swift/Demos/CandleStickChartViewController.swift b/ChartsDemo-iOS/Swift/Demos/CandleStickChartViewController.swift index c47b82b0a4..9525573a3b 100644 --- a/ChartsDemo-iOS/Swift/Demos/CandleStickChartViewController.swift +++ b/ChartsDemo-iOS/Swift/Demos/CandleStickChartViewController.swift @@ -108,17 +108,22 @@ class CandleStickChartViewController: DemoBaseViewController { override func optionTapped(_ option: Option) { switch option { case .toggleShadowColorSameAsCandle: - for case let set as CandleChartDataSet in chartView.data! { - set.shadowColorSameAsCandle = !set.shadowColorSameAsCandle + if let datasets = chartView.data?.dataSets as? [CandleChartDataSet] { + for set in datasets { + set.shadowColorSameAsCandle.toggle() + } + + chartView.notifyDataSetChanged() } - chartView.notifyDataSetChanged() + case .toggleShowCandleBar: if let datasets = chartView.data?.dataSets as? [CandleChartDataSet] { for set in datasets { set.showCandleBar.toggle() } + + chartView.notifyDataSetChanged() } - chartView.notifyDataSetChanged() default: super.handleOption(option, forChartView: chartView) }