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

Implemented support for rotated labels on the x-axis #513

Merged
merged 1 commit into from
Oct 27, 2015
Merged
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
4 changes: 2 additions & 2 deletions Charts/Classes/Charts/BarLineChartViewBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions Charts/Classes/Charts/HorizontalBarChartView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public class HorizontalBarChartView: BarChartView
offsetBottom += _rightAxis.getRequiredHeightSpace()
}

let xlabelwidth = _xAxis.labelWidth
let xlabelwidth = _xAxis.labelRotatedWidth

if (_xAxis.isEnabled)
{
Expand Down Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion Charts/Classes/Charts/PieRadarChartViewBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public class PieRadarChartViewBase: ChartViewBase

if x.isEnabled && x.drawLabelsEnabled
{
minOffset = max(minOffset, x.labelWidth)
minOffset = max(minOffset, x.labelRotatedWidth)
}
}

Expand Down
2 changes: 1 addition & 1 deletion Charts/Classes/Charts/RadarChartView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 16 additions & 1 deletion Charts/Classes/Components/ChartXAxis.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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
Expand All @@ -90,6 +103,8 @@ public class ChartXAxis: ChartAxisBase
public override init()
{
super.init()

self.yOffset = 4.0;
}

public override func getLongestLabel() -> String
Expand Down
43 changes: 27 additions & 16 deletions Charts/Classes/Renderers/ChartXAxisRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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))
}
}

Expand Down Expand Up @@ -124,14 +134,15 @@ 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

let labelAttrs = [NSFontAttributeName: _xAxis.labelFont,
NSForegroundColorAttributeName: _xAxis.labelTextColor,
NSParagraphStyleAttributeName: paraStyle]
let labelRotationAngleRadians = _xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD

let valueToPixelMatrix = transformer.valueToPixelMatrix

Expand Down Expand Up @@ -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())
Expand Down
5 changes: 3 additions & 2 deletions Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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
Expand Down Expand Up @@ -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)
}
}
}
Expand Down
42 changes: 26 additions & 16 deletions Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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())
Expand Down
10 changes: 6 additions & 4 deletions Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down
Loading