diff --git a/Charts/Classes/Charts/BarLineChartViewBase.swift b/Charts/Classes/Charts/BarLineChartViewBase.swift index f99944c448..556a95b8ac 100644 --- a/Charts/Classes/Charts/BarLineChartViewBase.swift +++ b/Charts/Classes/Charts/BarLineChartViewBase.swift @@ -448,7 +448,7 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar if (xAxis.isEnabled && xAxis.isDrawLabelsEnabled) { - let xlabelheight = xAxis.labelHeight * 2.0 + let xlabelheight = xAxis.labelRotatedHeight + xAxis.yOffset // offsets for x-labels if (xAxis.labelPosition == .Bottom) @@ -493,7 +493,7 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar if (!_xAxis.isAxisModulusCustom) { - _xAxis.axisLabelModulus = Int(ceil((CGFloat(_data.xValCount) * _xAxis.labelWidth) / (_viewPortHandler.contentWidth * _viewPortHandler.touchMatrix.a))) + _xAxis.axisLabelModulus = Int(ceil((CGFloat(_data.xValCount) * _xAxis.labelRotatedWidth) / (_viewPortHandler.contentWidth * _viewPortHandler.touchMatrix.a))) } if (_xAxis.axisLabelModulus < 1) diff --git a/Charts/Classes/Charts/HorizontalBarChartView.swift b/Charts/Classes/Charts/HorizontalBarChartView.swift index 0550cf159a..4f05841592 100644 --- a/Charts/Classes/Charts/HorizontalBarChartView.swift +++ b/Charts/Classes/Charts/HorizontalBarChartView.swift @@ -87,7 +87,7 @@ public class HorizontalBarChartView: BarChartView offsetBottom += _rightAxis.getRequiredHeightSpace() } - let xlabelwidth = _xAxis.labelWidth + let xlabelwidth = _xAxis.labelRotatedWidth if (_xAxis.isEnabled) { @@ -130,7 +130,7 @@ public class HorizontalBarChartView: BarChartView internal override func calcModulus() { - _xAxis.axisLabelModulus = Int(ceil((CGFloat(_data.xValCount) * _xAxis.labelHeight) / (_viewPortHandler.contentHeight * viewPortHandler.touchMatrix.d))) + _xAxis.axisLabelModulus = Int(ceil((CGFloat(_data.xValCount) * _xAxis.labelRotatedHeight) / (_viewPortHandler.contentHeight * viewPortHandler.touchMatrix.d))) if (_xAxis.axisLabelModulus < 1) { diff --git a/Charts/Classes/Charts/PieRadarChartViewBase.swift b/Charts/Classes/Charts/PieRadarChartViewBase.swift index b32c2fd6eb..f3bf950675 100755 --- a/Charts/Classes/Charts/PieRadarChartViewBase.swift +++ b/Charts/Classes/Charts/PieRadarChartViewBase.swift @@ -219,7 +219,7 @@ public class PieRadarChartViewBase: ChartViewBase if x.isEnabled && x.drawLabelsEnabled { - minOffset = max(minOffset, x.labelWidth) + minOffset = max(minOffset, x.labelRotatedWidth) } } diff --git a/Charts/Classes/Charts/RadarChartView.swift b/Charts/Classes/Charts/RadarChartView.swift index cfb2465376..6964fd0ee8 100644 --- a/Charts/Classes/Charts/RadarChartView.swift +++ b/Charts/Classes/Charts/RadarChartView.swift @@ -264,7 +264,7 @@ public class RadarChartView: PieRadarChartViewBase internal override var requiredBaseOffset: CGFloat { - return _xAxis.isEnabled && _xAxis.isDrawLabelsEnabled ? _xAxis.labelWidth : 10.0 + return _xAxis.isEnabled && _xAxis.isDrawLabelsEnabled ? _xAxis.labelRotatedWidth : 10.0 } public override var radius: CGFloat diff --git a/Charts/Classes/Components/ChartXAxis.swift b/Charts/Classes/Components/ChartXAxis.swift index f55a9ee856..d0f0aeb048 100644 --- a/Charts/Classes/Components/ChartXAxis.swift +++ b/Charts/Classes/Components/ChartXAxis.swift @@ -28,9 +28,22 @@ public class ChartXAxis: ChartAxisBase } public var values = [String?]() + + /// width of the x-axis labels in pixels - this is automatically calculated by the computeAxis() methods in the renderers public var labelWidth = CGFloat(1.0) + + /// height of the x-axis labels in pixels - this is automatically calculated by the computeAxis() methods in the renderers public var labelHeight = CGFloat(1.0) + /// width of the (rotated) x-axis labels in pixels - this is automatically calculated by the computeAxis() methods in the renderers + public var labelRotatedWidth = CGFloat(1.0) + + /// height of the (rotated) x-axis labels in pixels - this is automatically calculated by the computeAxis() methods in the renderers + public var labelRotatedHeight = CGFloat(1.0) + + /// This is the angle for drawing the X axis labels (in degrees) + public var labelRotationAngle = CGFloat(0.0) + /// the space that should be left out (in characters) between the x-axis labels /// This only applies if the number of labels that will be skipped in between drawn axis labels is not custom set. /// @@ -73,7 +86,7 @@ public class ChartXAxis: ChartAxisBase public var labelPosition = XAxisLabelPosition.Top /// if set to true, word wrapping the labels will be enabled. - /// word wrapping is done using `(value width * labelWidth)` + /// word wrapping is done using `(value width * labelRotatedWidth)` /// /// *Note: currently supports all charts except pie/radar/horizontal-bar* public var wordWrapEnabled = false @@ -90,6 +103,8 @@ public class ChartXAxis: ChartAxisBase public override init() { super.init() + + self.yOffset = 4.0; } public override func getLongestLabel() -> String diff --git a/Charts/Classes/Renderers/ChartXAxisRenderer.swift b/Charts/Classes/Renderers/ChartXAxisRenderer.swift index bb03cd6398..cae9697655 100644 --- a/Charts/Classes/Renderers/ChartXAxisRenderer.swift +++ b/Charts/Classes/Renderers/ChartXAxisRenderer.swift @@ -39,8 +39,18 @@ public class ChartXAxisRenderer: ChartAxisRendererBase let widthText = a as NSString - _xAxis.labelWidth = widthText.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]).width - _xAxis.labelHeight = _xAxis.labelFont.lineHeight + let labelSize = widthText.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]) + + let labelWidth = labelSize.width + let labelHeight = labelSize.height + + let labelRotatedSize = ChartUtils.sizeOfRotatedRectangle(labelSize, degrees: _xAxis.labelRotationAngle) + + _xAxis.labelWidth = labelWidth + _xAxis.labelHeight = labelHeight + _xAxis.labelRotatedWidth = labelRotatedSize.width + _xAxis.labelRotatedHeight = labelRotatedSize.height + _xAxis.values = xValues } @@ -51,28 +61,28 @@ public class ChartXAxisRenderer: ChartAxisRendererBase return } - let yoffset = CGFloat(4.0) + let yOffset = _xAxis.yOffset if (_xAxis.labelPosition == .Top) { - drawLabels(context: context, pos: viewPortHandler.offsetTop - _xAxis.labelHeight - yoffset) + drawLabels(context: context, pos: viewPortHandler.contentTop - yOffset, anchor: CGPoint(x: 0.5, y: 1.0)) } - else if (_xAxis.labelPosition == .Bottom) + else if (_xAxis.labelPosition == .TopInside) { - drawLabels(context: context, pos: viewPortHandler.contentBottom + yoffset * 1.5) + drawLabels(context: context, pos: viewPortHandler.contentTop + yOffset + _xAxis.labelRotatedHeight, anchor: CGPoint(x: 0.5, y: 1.0)) } - else if (_xAxis.labelPosition == .BottomInside) + else if (_xAxis.labelPosition == .Bottom) { - drawLabels(context: context, pos: viewPortHandler.contentBottom - _xAxis.labelHeight - yoffset) + drawLabels(context: context, pos: viewPortHandler.contentBottom + yOffset, anchor: CGPoint(x: 0.5, y: 0.0)) } - else if (_xAxis.labelPosition == .TopInside) + else if (_xAxis.labelPosition == .BottomInside) { - drawLabels(context: context, pos: viewPortHandler.offsetTop + yoffset) + drawLabels(context: context, pos: viewPortHandler.contentBottom - yOffset - _xAxis.labelRotatedHeight, anchor: CGPoint(x: 0.5, y: 0.0)) } else { // BOTH SIDED - drawLabels(context: context, pos: viewPortHandler.offsetTop - _xAxis.labelHeight - yoffset) - drawLabels(context: context, pos: viewPortHandler.contentBottom + yoffset * 1.6) + drawLabels(context: context, pos: viewPortHandler.contentTop - yOffset, anchor: CGPoint(x: 0.5, y: 1.0)) + drawLabels(context: context, pos: viewPortHandler.contentBottom + yOffset, anchor: CGPoint(x: 0.5, y: 0.0)) } } @@ -124,7 +134,7 @@ public class ChartXAxisRenderer: ChartAxisRendererBase } /// draws the x-labels on the specified y-position - internal func drawLabels(context context: CGContext, pos: CGFloat) + internal func drawLabels(context context: CGContext, pos: CGFloat, anchor: CGPoint) { let paraStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle paraStyle.alignment = .Center @@ -132,6 +142,7 @@ public class ChartXAxisRenderer: ChartAxisRendererBase let labelAttrs = [NSFontAttributeName: _xAxis.labelFont, NSForegroundColorAttributeName: _xAxis.labelTextColor, NSParagraphStyleAttributeName: paraStyle] + let labelRotationAngleRadians = _xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD let valueToPixelMatrix = transformer.valueToPixelMatrix @@ -180,15 +191,15 @@ public class ChartXAxisRenderer: ChartAxisRendererBase } } - drawLabel(context: context, label: label!, xIndex: i, x: position.x, y: pos, align: .Center, attributes: labelAttrs, constrainedToSize: labelMaxSize) + drawLabel(context: context, label: label!, xIndex: i, x: position.x, y: pos, attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians) } } } - internal func drawLabel(context context: CGContext, label: String, xIndex: Int, x: CGFloat, y: CGFloat, align: NSTextAlignment, attributes: [String: NSObject], constrainedToSize: CGSize) + internal func drawLabel(context context: CGContext, label: String, xIndex: Int, x: CGFloat, y: CGFloat, attributes: [String: NSObject], constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) { let formattedLabel = _xAxis.valueFormatter?.stringForXValue(xIndex, original: label, viewPortHandler: viewPortHandler) ?? label - ChartUtils.drawMultilineText(context: context, text: formattedLabel, point: CGPoint(x: x, y: y), align: align, attributes: attributes, constrainedToSize: constrainedToSize) + ChartUtils.drawMultilineText(context: context, text: formattedLabel, point: CGPoint(x: x, y: y), attributes: attributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians) } private var _gridLineSegmentsBuffer = [CGPoint](count: 2, repeatedValue: CGPoint()) diff --git a/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift b/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift index ec0a6794d5..59012fba1a 100644 --- a/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift +++ b/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift @@ -27,7 +27,7 @@ public class ChartXAxisRendererBarChart: ChartXAxisRenderer } /// draws the x-labels on the specified y-position - internal override func drawLabels(context context: CGContext, pos: CGFloat) + internal override func drawLabels(context context: CGContext, pos: CGFloat, anchor: CGPoint) { if (_chart.data === nil) { @@ -40,6 +40,7 @@ public class ChartXAxisRendererBarChart: ChartXAxisRenderer let labelAttrs = [NSFontAttributeName: _xAxis.labelFont, NSForegroundColorAttributeName: _xAxis.labelTextColor, NSParagraphStyleAttributeName: paraStyle] + let labelRotationAngleRadians = _xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD let barData = _chart.data as! BarChartData let step = barData.dataSetCount @@ -99,7 +100,7 @@ public class ChartXAxisRendererBarChart: ChartXAxisRenderer } } - drawLabel(context: context, label: label!, xIndex: i, x: position.x, y: pos, align: .Center, attributes: labelAttrs, constrainedToSize: labelMaxSize) + drawLabel(context: context, label: label!, xIndex: i, x: position.x, y: pos, attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians) } } } diff --git a/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift b/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift index 44e7182182..82bc411902 100644 --- a/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift +++ b/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift @@ -27,9 +27,18 @@ public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart _xAxis.values = xValues let longest = _xAxis.getLongestLabel() as NSString - let longestSize = longest.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]) - _xAxis.labelWidth = floor(longestSize.width + _xAxis.xOffset * 3.5) - _xAxis.labelHeight = longestSize.height + + let labelSize = longest.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]) + + let labelWidth = floor(labelSize.width + _xAxis.xOffset * 3.5) + let labelHeight = labelSize.height + + let labelRotatedSize = ChartUtils.sizeOfRotatedRectangle(rectangleWidth: labelSize.width, rectangleHeight: labelHeight, degrees: _xAxis.labelRotationAngle) + + _xAxis.labelWidth = labelWidth + _xAxis.labelHeight = labelHeight + _xAxis.labelRotatedWidth = round(labelRotatedSize.width + _xAxis.xOffset * 3.5) + _xAxis.labelRotatedHeight = round(labelRotatedSize.height) } public override func renderAxisLabels(context context: CGContext) @@ -43,32 +52,33 @@ public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart if (_xAxis.labelPosition == .Top) { - drawLabels(context: context, pos: viewPortHandler.contentRight + xoffset, align: .Left) + drawLabels(context: context, pos: viewPortHandler.contentRight + xoffset, anchor: CGPoint(x: 0.0, y: 0.5)) } - else if (_xAxis.labelPosition == .Bottom) + else if (_xAxis.labelPosition == .TopInside) { - drawLabels(context: context, pos: viewPortHandler.contentLeft - xoffset, align: .Right) + drawLabels(context: context, pos: viewPortHandler.contentRight - xoffset, anchor: CGPoint(x: 1.0, y: 0.5)) } - else if (_xAxis.labelPosition == .BottomInside) + else if (_xAxis.labelPosition == .Bottom) { - drawLabels(context: context, pos: viewPortHandler.contentLeft + xoffset, align: .Left) + drawLabels(context: context, pos: viewPortHandler.contentLeft - xoffset, anchor: CGPoint(x: 1.0, y: 0.5)) } - else if (_xAxis.labelPosition == .TopInside) + else if (_xAxis.labelPosition == .BottomInside) { - drawLabels(context: context, pos: viewPortHandler.contentRight - xoffset, align: .Right) + drawLabels(context: context, pos: viewPortHandler.contentLeft + xoffset, anchor: CGPoint(x: 0.0, y: 0.5)) } else { // BOTH SIDED - drawLabels(context: context, pos: viewPortHandler.contentLeft - xoffset, align: .Right) - drawLabels(context: context, pos: viewPortHandler.contentRight + xoffset, align: .Left) + drawLabels(context: context, pos: viewPortHandler.contentRight + xoffset, anchor: CGPoint(x: 0.0, y: 0.5)) + drawLabels(context: context, pos: viewPortHandler.contentLeft - xoffset, anchor: CGPoint(x: 1.0, y: 0.5)) } } /// draws the x-labels on the specified y-position - internal func drawLabels(context context: CGContext, pos: CGFloat, align: NSTextAlignment) + internal override func drawLabels(context context: CGContext, pos: CGFloat, anchor: CGPoint) { let labelFont = _xAxis.labelFont let labelTextColor = _xAxis.labelTextColor + let labelRotationAngleRadians = _xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD // pre allocate to save performance (dont allocate in loop) var position = CGPoint(x: 0.0, y: 0.0) @@ -98,15 +108,15 @@ public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart if (viewPortHandler.isInBoundsY(position.y)) { - drawLabel(context: context, label: label!, xIndex: i, x: pos, y: position.y - _xAxis.labelHeight / 2.0, align: align, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]) + drawLabel(context: context, label: label!, xIndex: i, x: pos, y: position.y, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor], anchor: anchor, angleRadians: labelRotationAngleRadians) } } } - internal func drawLabel(context context: CGContext, label: String, xIndex: Int, x: CGFloat, y: CGFloat, align: NSTextAlignment, attributes: [String: NSObject]) + internal func drawLabel(context context: CGContext, label: String, xIndex: Int, x: CGFloat, y: CGFloat, attributes: [String: NSObject], anchor: CGPoint, angleRadians: CGFloat) { let formattedLabel = _xAxis.valueFormatter?.stringForXValue(xIndex, original: label, viewPortHandler: viewPortHandler) ?? label - ChartUtils.drawText(context: context, text: formattedLabel, point: CGPoint(x: x, y: y), align: align, attributes: attributes) + ChartUtils.drawText(context: context, text: formattedLabel, point: CGPoint(x: x, y: y), attributes: attributes, anchor: anchor, angleRadians: angleRadians) } private var _gridLineSegmentsBuffer = [CGPoint](count: 2, repeatedValue: CGPoint()) diff --git a/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift b/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift index 1444cb62de..3f0073409f 100644 --- a/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift +++ b/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift @@ -35,6 +35,8 @@ public class ChartXAxisRendererRadarChart: ChartXAxisRenderer let labelFont = _xAxis.labelFont let labelTextColor = _xAxis.labelTextColor + let labelRotationAngleRadians = _xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD + let drawLabelAnchor = CGPoint(x: 0.5, y: 0.0) let sliceangle = _chart.sliceAngle @@ -55,16 +57,16 @@ public class ChartXAxisRendererRadarChart: ChartXAxisRenderer let angle = (sliceangle * CGFloat(i) + _chart.rotationAngle) % 360.0 - let p = ChartUtils.getPosition(center: center, dist: CGFloat(_chart.yRange) * factor + _xAxis.labelWidth / 2.0, angle: angle) + let p = ChartUtils.getPosition(center: center, dist: CGFloat(_chart.yRange) * factor + _xAxis.labelRotatedWidth / 2.0, angle: angle) - drawLabel(context: context, label: label!, xIndex: i, x: p.x, y: p.y - _xAxis.labelHeight / 2.0, align: .Center, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]) + drawLabel(context: context, label: label!, xIndex: i, x: p.x, y: p.y - _xAxis.labelRotatedHeight / 2.0, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor], anchor: drawLabelAnchor, angleRadians: labelRotationAngleRadians) } } - internal func drawLabel(context context: CGContext, label: String, xIndex: Int, x: CGFloat, y: CGFloat, align: NSTextAlignment, attributes: [String: NSObject]) + internal func drawLabel(context context: CGContext, label: String, xIndex: Int, x: CGFloat, y: CGFloat, attributes: [String: NSObject], anchor: CGPoint, angleRadians: CGFloat) { let formattedLabel = _xAxis.valueFormatter?.stringForXValue(xIndex, original: label, viewPortHandler: viewPortHandler) ?? label - ChartUtils.drawText(context: context, text: formattedLabel, point: CGPoint(x: x, y: y), align: align, attributes: attributes) + ChartUtils.drawText(context: context, text: formattedLabel, point: CGPoint(x: x, y: y), attributes: attributes, anchor: anchor, angleRadians: angleRadians) } public override func renderLimitLines(context context: CGContext) diff --git a/Charts/Classes/Utils/ChartUtils.swift b/Charts/Classes/Utils/ChartUtils.swift index 697b3b388e..63b74b14c2 100755 --- a/Charts/Classes/Utils/ChartUtils.swift +++ b/Charts/Classes/Utils/ChartUtils.swift @@ -130,36 +130,118 @@ public class ChartUtils { point.x -= text.sizeWithAttributes(attributes).width } - + UIGraphicsPushContext(context) + (text as NSString).drawAtPoint(point, withAttributes: attributes) + UIGraphicsPopContext() } - internal class func drawMultilineText(context context: CGContext, text: String, knownTextSize: CGSize, point: CGPoint, align: NSTextAlignment, attributes: [String : AnyObject]?, constrainedToSize: CGSize) + public class func drawText(context context: CGContext, text: String, point: CGPoint, attributes: [String : AnyObject]?, anchor: CGPoint, angleRadians: CGFloat) { - var rect = CGRect(origin: CGPoint(), size: knownTextSize) - rect.origin.x += point.x - rect.origin.y += point.y + var drawOffset = CGPoint() - if (align == .Center) + UIGraphicsPushContext(context) + + if angleRadians != 0.0 { - rect.origin.x -= rect.size.width / 2.0 + let size = text.sizeWithAttributes(attributes) + + // Move the text drawing rect in a way that it always rotates around its center + drawOffset.x = -size.width * 0.5 + drawOffset.y = -size.height * 0.5 + + var translate = point + + // Move the "outer" rect relative to the anchor, assuming its centered + if anchor.x != 0.5 || anchor.y != 0.5 + { + let rotatedSize = sizeOfRotatedRectangle(size, radians: angleRadians) + + translate.x -= rotatedSize.width * (anchor.x - 0.5) + translate.y -= rotatedSize.height * (anchor.y - 0.5) + } + + CGContextSaveGState(context) + CGContextTranslateCTM(context, translate.x, translate.y) + CGContextRotateCTM(context, angleRadians) + + (text as NSString).drawAtPoint(drawOffset, withAttributes: attributes) + + CGContextRestoreGState(context) } - else if (align == .Right) + else { - rect.origin.x -= rect.size.width + if anchor.x != 0.0 || anchor.y != 0.0 + { + let size = text.sizeWithAttributes(attributes) + + drawOffset.x = -size.width * anchor.x + drawOffset.y = -size.height * anchor.y + } + + drawOffset.x += point.x + drawOffset.y += point.y + + (text as NSString).drawAtPoint(drawOffset, withAttributes: attributes) } + UIGraphicsPopContext() + } + + internal class func drawMultilineText(context context: CGContext, text: String, knownTextSize: CGSize, point: CGPoint, attributes: [String : AnyObject]?, constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) + { + var rect = CGRect(origin: CGPoint(), size: knownTextSize) + UIGraphicsPushContext(context) - (text as NSString).drawWithRect(rect, options: .UsesLineFragmentOrigin, attributes: attributes, context: nil) + + if angleRadians != 0.0 + { + // Move the text drawing rect in a way that it always rotates around its center + rect.origin.x = -knownTextSize.width * 0.5 + rect.origin.y = -knownTextSize.height * 0.5 + + var translate = point + + // Move the "outer" rect relative to the anchor, assuming its centered + if anchor.x != 0.5 || anchor.y != 0.5 + { + let rotatedSize = sizeOfRotatedRectangle(knownTextSize, radians: angleRadians) + + translate.x -= rotatedSize.width * (anchor.x - 0.5) + translate.y -= rotatedSize.height * (anchor.y - 0.5) + } + + CGContextSaveGState(context) + CGContextTranslateCTM(context, translate.x, translate.y) + CGContextRotateCTM(context, angleRadians) + + (text as NSString).drawWithRect(rect, options: .UsesLineFragmentOrigin, attributes: attributes, context: nil) + + CGContextRestoreGState(context) + } + else + { + if anchor.x != 0.0 || anchor.y != 0.0 + { + rect.origin.x = -knownTextSize.width * anchor.x + rect.origin.y = -knownTextSize.height * anchor.y + } + + rect.origin.x += point.x + rect.origin.y += point.y + + (text as NSString).drawWithRect(rect, options: .UsesLineFragmentOrigin, attributes: attributes, context: nil) + } + UIGraphicsPopContext() } - internal class func drawMultilineText(context context: CGContext, text: String, point: CGPoint, align: NSTextAlignment, attributes: [String : AnyObject]?, constrainedToSize: CGSize) + internal class func drawMultilineText(context context: CGContext, text: String, point: CGPoint, attributes: [String : AnyObject]?, constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) { let rect = text.boundingRectWithSize(constrainedToSize, options: .UsesLineFragmentOrigin, attributes: attributes, context: nil) - drawMultilineText(context: context, text: text, knownTextSize: rect.size, point: point, align: align, attributes: attributes, constrainedToSize: constrainedToSize) + drawMultilineText(context: context, text: text, knownTextSize: rect.size, point: point, attributes: attributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians) } /// - returns: an angle between 0.0 < 360.0 (not less than zero, less than 360) @@ -189,6 +271,31 @@ public class ChartUtils return _defaultValueFormatter } + internal class func sizeOfRotatedRectangle(rectangleSize: CGSize, degrees: CGFloat) -> CGSize + { + let radians = degrees * Math.FDEG2RAD + return sizeOfRotatedRectangle(rectangleWidth: rectangleSize.width, rectangleHeight: rectangleSize.height, radians: radians) + } + + internal class func sizeOfRotatedRectangle(rectangleSize: CGSize, radians: CGFloat) -> CGSize + { + return sizeOfRotatedRectangle(rectangleWidth: rectangleSize.width, rectangleHeight: rectangleSize.height, radians: radians) + } + + internal class func sizeOfRotatedRectangle(rectangleWidth rectangleWidth: CGFloat, rectangleHeight: CGFloat, degrees: CGFloat) -> CGSize + { + let radians = degrees * Math.FDEG2RAD + return sizeOfRotatedRectangle(rectangleWidth: rectangleWidth, rectangleHeight: rectangleHeight, radians: radians) + } + + internal class func sizeOfRotatedRectangle(rectangleWidth rectangleWidth: CGFloat, rectangleHeight: CGFloat, radians: CGFloat) -> CGSize + { + return CGSize( + width: abs(rectangleWidth * cos(radians)) + abs(rectangleHeight * sin(radians)), + height: abs(rectangleWidth * sin(radians)) + abs(rectangleHeight * cos(radians)) + ) + } + /// MARK: - Bridging functions internal class func bridgedObjCGetUIColorArray (swift array: [UIColor?]) -> [NSObject]