Skip to content

Commit

Permalink
[Feature] added ability to rotate ChartLimitLine label (ChartsOrg#5085)
Browse files Browse the repository at this point in the history
* added labelRotationAngle to ChartLimitLine

* added limit line label rotation tests

* fixed the code style
labelLineRotated_Width/Height got the same position

* generated test images for apple TV

* add snapshots on x64 arch for tvOS and iOS

---------

Co-authored-by: Xuan <liuxuan30@noreply.github.com>
  • Loading branch information
berbaspin and Xuan authored Aug 22, 2023
1 parent 52be297 commit e516b04
Show file tree
Hide file tree
Showing 30 changed files with 120 additions and 58 deletions.
1 change: 1 addition & 0 deletions Source/Charts/Components/ChartLimitLine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ open class ChartLimitLine: ComponentBase
@objc open var drawLabelEnabled = true
@objc open var label = ""
@objc open var labelPosition = LabelPosition.rightTop
@objc open var labelRotationAngle = CGFloat(0.0)

public override init()
{
Expand Down
39 changes: 22 additions & 17 deletions Source/Charts/Renderers/XAxisRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -449,40 +449,45 @@ open class XAxisRenderer: NSObject, AxisRenderer
// if drawing the limit-value label is enabled
guard limitLine.drawLabelEnabled, !label.isEmpty else { return }

let labelLineHeight = limitLine.valueFont.lineHeight
let labelLineSize = label.size(withAttributes: [.font: limitLine.valueFont])
let labelLineRotatedSize = labelLineSize.rotatedBy(degrees: limitLine.labelRotationAngle)
let labelLineRotatedWidth = labelLineRotatedSize.width
let labelLineRotatedHeight = labelLineRotatedSize.height

let xOffset: CGFloat = limitLine.lineWidth + limitLine.xOffset
let labelRotationAngleRadians = limitLine.labelRotationAngle.DEG2RAD

let align: TextAlignment
let point: CGPoint
let anchor = CGPoint(x: 0.0, y: 0.0)

switch limitLine.labelPosition
{
switch limitLine.labelPosition {
case .rightTop:
align = .left
point = CGPoint(x: position.x + xOffset,
y: viewPortHandler.contentTop + yOffset)
point = CGPoint(
x: position.x + xOffset,
y: viewPortHandler.contentTop + yOffset)

case .rightBottom:
align = .left
point = CGPoint(x: position.x + xOffset,
y: viewPortHandler.contentBottom - labelLineHeight - yOffset)
y: viewPortHandler.contentBottom - labelLineRotatedHeight - yOffset)

case .leftTop:
align = .right
point = CGPoint(x: position.x - xOffset,
point = CGPoint(x: position.x - labelLineRotatedWidth - xOffset,
y: viewPortHandler.contentTop + yOffset)

case .leftBottom:
align = .right
point = CGPoint(x: position.x - xOffset,
y: viewPortHandler.contentBottom - labelLineHeight - yOffset)
point = CGPoint(x: position.x - labelLineRotatedWidth - xOffset,
y: viewPortHandler.contentBottom - labelLineRotatedHeight - yOffset)
}

let attributes: [NSAttributedString.Key : Any] = [
.font: limitLine.valueFont,
.foregroundColor: limitLine.valueTextColor
]

context.drawText(label,
at: point,
align: align,
attributes: [.font: limitLine.valueFont,
.foregroundColor: limitLine.valueTextColor])
anchor: anchor,
angleRadians: labelRotationAngleRadians,
attributes: attributes)
}
}
35 changes: 21 additions & 14 deletions Source/Charts/Renderers/XAxisRendererHorizontalBarChart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -249,41 +249,48 @@ open class XAxisRendererHorizontalBarChart: XAxisRenderer
// if drawing the limit-value label is enabled
if l.drawLabelEnabled, !label.isEmpty
{
let labelLineHeight = l.valueFont.lineHeight


let labelLineSize = label.size(withAttributes: [.font: l.valueFont])
let labelLineRotatedSize = labelLineSize.rotatedBy(degrees: l.labelRotationAngle)
let labelLineRotatedWidth = labelLineRotatedSize.width
let labelLineRotatedHeight = labelLineRotatedSize.height

let xOffset = 4.0 + l.xOffset
let yOffset = l.lineWidth + labelLineHeight + l.yOffset
let yOffset = l.lineWidth + labelLineRotatedHeight + l.yOffset
let labelRotationAngleRadians = l.labelRotationAngle.DEG2RAD

let align: TextAlignment
let point: CGPoint
let anchor = CGPoint(x: 0.0, y: 0.0)

switch l.labelPosition
{
case .rightTop:
align = .right
point = CGPoint(x: viewPortHandler.contentRight - xOffset,
point = CGPoint(x: viewPortHandler.contentRight - labelLineRotatedWidth - xOffset,
y: position.y - yOffset)

case .rightBottom:
align = .right
point = CGPoint(x: viewPortHandler.contentRight - xOffset,
y: position.y + yOffset - labelLineHeight)
point = CGPoint(x: viewPortHandler.contentRight - labelLineRotatedWidth - xOffset,
y: position.y - labelLineRotatedHeight + yOffset)

case .leftTop:
align = .left
point = CGPoint(x: viewPortHandler.contentLeft + xOffset,
y: position.y - yOffset)

case .leftBottom:
align = .left
point = CGPoint(x: viewPortHandler.contentLeft + xOffset,
y: position.y + yOffset - labelLineHeight)
y: position.y - labelLineRotatedHeight + yOffset)
}

let attributes: [NSAttributedString.Key : Any] = [
.font: l.valueFont,
.foregroundColor: l.valueTextColor
]

context.drawText(label,
at: point,
align: align,
attributes: [.font: l.valueFont, .foregroundColor: l.valueTextColor])
anchor: anchor,
angleRadians: labelRotationAngleRadians,
attributes: attributes)
}
}
}
Expand Down
34 changes: 20 additions & 14 deletions Source/Charts/Renderers/YAxisRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -295,41 +295,47 @@ open class YAxisRenderer: NSObject, AxisRenderer
// if drawing the limit-value label is enabled
guard l.drawLabelEnabled, !label.isEmpty else { continue }

let labelLineHeight = l.valueFont.lineHeight

let labelLineSize = label.size(withAttributes: [.font: l.valueFont])
let labelLineRotatedSize = labelLineSize.rotatedBy(degrees: l.labelRotationAngle)
let labelLineRotatedWidth = labelLineRotatedSize.width
let labelLineRotatedHeight = labelLineRotatedSize.height

let xOffset = 4.0 + l.xOffset
let yOffset = l.lineWidth + labelLineHeight + l.yOffset
let yOffset = l.lineWidth + labelLineRotatedHeight + l.yOffset
let labelRotationAngleRadians = l.labelRotationAngle.DEG2RAD

let align: TextAlignment
let point: CGPoint
let anchor = CGPoint(x: 0.0, y: 0.0)

switch l.labelPosition
{
case .rightTop:
align = .right
point = CGPoint(x: viewPortHandler.contentRight - xOffset,
point = CGPoint(x: viewPortHandler.contentRight - labelLineRotatedWidth - xOffset,
y: position.y - yOffset)

case .rightBottom:
align = .right
point = CGPoint(x: viewPortHandler.contentRight - xOffset,
y: position.y + yOffset - labelLineHeight)
point = CGPoint(x: viewPortHandler.contentRight - labelLineRotatedWidth - xOffset,
y: position.y - labelLineRotatedHeight + yOffset)

case .leftTop:
align = .left
point = CGPoint(x: viewPortHandler.contentLeft + xOffset,
y: position.y - yOffset)

case .leftBottom:
align = .left
point = CGPoint(x: viewPortHandler.contentLeft + xOffset,
y: position.y + yOffset - labelLineHeight)
y: position.y - labelLineRotatedHeight + yOffset)
}

let attributes: [NSAttributedString.Key : Any] = [
.font: l.valueFont,
.foregroundColor: l.valueTextColor
]

context.drawText(label,
at: point,
align: align,
attributes: [.font: l.valueFont, .foregroundColor: l.valueTextColor])
anchor: anchor,
angleRadians: labelRotationAngleRadians,
attributes: attributes)
}
}

Expand Down
32 changes: 19 additions & 13 deletions Source/Charts/Renderers/YAxisRendererHorizontalBarChart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -253,41 +253,47 @@ open class YAxisRendererHorizontalBarChart: YAxisRenderer
// if drawing the limit-value label is enabled
if l.drawLabelEnabled, !label.isEmpty
{
let labelLineHeight = l.valueFont.lineHeight

let labelLineSize = label.size(withAttributes: [.font: l.valueFont])
let labelLineRotatedSize = labelLineSize.rotatedBy(degrees: l.labelRotationAngle)
let labelLineRotatedWidth = labelLineRotatedSize.width
let labelLineRotatedHeight = labelLineRotatedSize.height

let xOffset = l.lineWidth + l.xOffset
let yOffset = 2.0 + l.yOffset
let labelRotationAngleRadians = l.labelRotationAngle.DEG2RAD

let align: TextAlignment
let point: CGPoint
let anchor = CGPoint(x: 0.0, y: 0.0)

switch l.labelPosition
{
case .rightTop:
align = .left
point = CGPoint(x: position.x + xOffset,
y: viewPortHandler.contentTop + yOffset)

case .rightBottom:
align = .left
point = CGPoint(x: position.x + xOffset,
y: viewPortHandler.contentBottom - labelLineHeight - yOffset)
y: viewPortHandler.contentBottom - labelLineRotatedHeight - yOffset)

case .leftTop:
align = .right
point = CGPoint(x: position.x - xOffset,
point = CGPoint(x: position.x - labelLineRotatedWidth - xOffset,
y: viewPortHandler.contentTop + yOffset)

case .leftBottom:
align = .right
point = CGPoint(x: position.x - xOffset,
y: viewPortHandler.contentBottom - labelLineHeight - yOffset)
point = CGPoint(x: position.x - labelLineRotatedWidth - xOffset,
y: viewPortHandler.contentBottom - labelLineRotatedHeight - yOffset)
}

let attributes: [NSAttributedString.Key : Any] = [
.font: l.valueFont,
.foregroundColor: l.valueTextColor
]

context.drawText(label,
at: point,
align: align,
attributes: [.font: l.valueFont, .foregroundColor: l.valueTextColor])
anchor: anchor,
angleRadians: labelRotationAngleRadians,
attributes: attributes)
}
}
}
Expand Down
37 changes: 37 additions & 0 deletions Tests/ChartsTests/LineChartTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,41 @@ class LineChartTests: XCTestCase {
dataSet.drawIconsEnabled = true
assertChartSnapshot(matching: chart)
}

func testLimitLineLabelRotatedBy180() {
addLimitLineWithRotatedAngle(labelPosition: .rightBottom, labelRotationAngle: 180)
assertChartSnapshot(matching: chart)
}

func testLimitLineLabelRotatedBy120() {
addLimitLineWithRotatedAngle(labelPosition: .rightTop, labelRotationAngle: 120)
assertChartSnapshot(matching: chart)
}

func testLimitLineLabelRotatedBy90() {
addLimitLineWithRotatedAngle(labelPosition: .leftTop, labelRotationAngle: 90)
assertChartSnapshot(matching: chart)
}

func testLimitLineLabelRotatedBy45() {
addLimitLineWithRotatedAngle(labelPosition: .leftBottom, labelRotationAngle: 45)
assertChartSnapshot(matching: chart)
}

func testLimitLineLabelRotatedBy30() {
addLimitLineWithRotatedAngle(labelPosition: .leftBottom, labelRotationAngle: 30)
assertChartSnapshot(matching: chart)
}

func testLimitLineLabelDefaultRotation() {
addLimitLineWithRotatedAngle(labelPosition: .rightTop, labelRotationAngle: 0)
assertChartSnapshot(matching: chart)
}

private func addLimitLineWithRotatedAngle(labelPosition: ChartLimitLine.LabelPosition, labelRotationAngle: CGFloat) {
let limitline = ChartLimitLine(limit: 50, label: "Limit Line")
limitline.labelPosition = labelPosition
limitline.labelRotationAngle = labelRotationAngle
chart.leftAxis.addLimitLine(limitline)
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e516b04

Please sign in to comment.