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

Barchart label offset when its text is too long #3800

Open
LeeJoey77 opened this issue Dec 27, 2018 · 8 comments
Open

Barchart label offset when its text is too long #3800

LeeJoey77 opened this issue Dec 27, 2018 · 8 comments
Labels

Comments

@LeeJoey77
Copy link

What did you do?

barchart label offset when its text is too long, and may overlap next label if long enough.

Charts version: V3.2.1
Xcode version: 10.1
Swift version: 4.2

Demo Project

`

let xAxis = chartView.xAxis
xAxis.axisLineWidth = 1
xAxis.labelPosition = .bottom
xAxis.labelRotationAngle = CGFloat(-50)
xAxis.labelRotatedHeight = CGFloat(120)
xAxis.drawGridLinesEnabled = false
xAxis.forceLabelsEnabled = false
xAxis.labelTextColor = UIColor.black
xAxis.labelFont = UIFont.systemFont(ofSize: 13)
xAxis.granularity = 1
xAxis.labelCount = xValues.count
xAxis.valueFormatter = self`

`

func stringForValue(_ value: Double, axis: AxisBase?) -> String {
if Int(value) == 4 {
return "fadafadfadfafdafafadffadf"
}
return xValues[(Int(value)) % xValues.count]
}
`

2018-12-27 1 18 31

@liuxuan30
Copy link
Member

where is the overlap? Not seeing it

@LeeJoey77
Copy link
Author

2018-12-27 5 36 03

@LeeJoey77
Copy link
Author

@liuxuan30 if text is long enough, it will overlap

@liuxuan30
Copy link
Member

hmm.. seems a bug that didn't get the right rotated width

@liuxuan30 liuxuan30 added the bug label Jan 3, 2019
@liuxuan30
Copy link
Member

but I'm not sure when we have time to look into. if you have time and willing to try it out, please go ahead and maybe you can find the fix. or just wait :(

@anton-filimonov
Copy link

anton-filimonov commented Jan 3, 2019

I've taken a look into renderer and found out that the reason of such behaviour is in selection of anchor point. For labelPosition == .bottom the value of anchor for label is (0.5, 0.0) which is ok when label is placed horizontally but gives such artifacts when label is rotated.
But what is the correct way to handle this?
At first look seems like moving anchor point to (1.0, 0.0) when normalized (translated to range -90 - 90) angle is <0 and to (0.0, 0.0) in opposite case is pretty much correct but I'm not sure everyone would agree with that.
Applying this change we get result like this (not ideal because only the rightmost point of the label is under the corresponding grid line but this also could be fixed):
image
IMHO it'd be better to correct also the way anchor point is used on rotation to make it's behavior similar to how anchor point and transformation works with layers. In this case method drawMultilineText(...) would change to this:

internal class func drawMultilineText(context: CGContext, text: String, knownTextSize: CGSize, point: CGPoint, attributes: [NSAttributedString.Key : Any]?, constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat)
    {
        let rect = CGRect(origin: CGPoint(x: -anchor.x * knownTextSize.width,
                                          y: -anchor.y * knownTextSize.height),
                          size: knownTextSize)
        
        NSUIGraphicsPushContext(context)
        
        context.saveGState()
        
        context.translateBy(x: point.x, y: point.y)
        context.rotate(by: angleRadians)
        (text as NSString).draw(with: rect, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)
        
        context.restoreGState()
        
        NSUIGraphicsPopContext()
    }

And the result would look like this (now top right corner of the text frame is right below the grid line):
image

@jjatie @liuxuan30 what do you think of these two changes? (I can create a PR, but it requires some more work then I've already done on it, so I'd prefer discussing it first)

@LeeJoey77 You can make these changes in your app by overriding renderAxisLabels(context:) method of the XAxisRenderer

@liuxuan30
Copy link
Member

liuxuan30 commented Jan 4, 2019

@anton-filimonov thanks for helping out!
I'm not sure if your change is good or not; the rotation calculation begins in

    @objc open func computeSize()
    {
        guard let
            xAxis = self.axis as? XAxis
            else { return }
        
        let longest = xAxis.getLongestLabel()
        
        let labelSize = longest.size(withAttributes: [NSAttributedString.Key.font: xAxis.labelFont])
        
        let labelWidth = labelSize.width
        let labelHeight = labelSize.height
        
        let labelRotatedSize = labelSize.rotatedBy(degrees: xAxis.labelRotationAngle)
        
        xAxis.labelWidth = labelWidth
        xAxis.labelHeight = labelHeight
        xAxis.labelRotatedWidth = labelRotatedSize.width
        xAxis.labelRotatedHeight = labelRotatedSize.height
    }

I'm thinking the bug comes from renderAxisLabels() does not respect the rotated label size causing the overlap. By default I remember in Charts 2.x it will use label width to calculate somewhere how many labels can actually display without overlap. But I couldn't find the place right now. Maybe it's removed since 3.0 so causing the overlap?

If you were able to use the rotated label size, you don't have to add a parameter?

anyway, if we can fix the overlap issue, you are welcome to file a PR that you think is best suitable.

BTW, I search a little that seems overlaping is always an issue that's not fixed no matter what. e.g. #1969. if we can fix them once and for all that's even better.

but take your time. we don't want to force you

@liuxuan30
Copy link
Member

liuxuan30 commented Jan 4, 2019

I took a look that what I said

By default I remember in Charts 2.x it will use label width to calculate somewhere how many labels can actually display without overlap

seems totally gone or I am wrong.

Currently labelCount is entirely a property for user to control. It will just display without considering overlap, so it seems user's duty to not to use too many labels. but the problem exists that if the formatted label is too long, it will overlap anyway?

@jjatie should we bring the feature to avoid overlap? or you think it's users' duty to control the label count and overlap? and we only have to care about if @anton-filimonov's change is valid to address some bugs in rotation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants