Charts: XAxis рд▓реЗрдмрд▓ рдЪрд╛рд░реНрдЯ 3.0 рдореЗрдВ рдУрд╡рд░рд▓реИрдк рд╣реЛрддреЗ рд╣реИрдВ

рдХреЛ рдирд┐рд░реНрдорд┐рдд 16 рджрд┐рд╕ре░ 2016  ┬╖  11рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: danielgindi/Charts

рдЪрд╛рд░реНрдЯ 2.x рдореЗрдВ, xAxis рдиреЗ рдЗрд╕реЗ рд▓реЗрдмрд▓ рдХрд┐рдпрд╛ рд╣реИ рдЬреЛ рдХрд┐ рд╕рдмрд╕реЗ рдмрдбрд╝реЗ рд▓реЗрдмрд▓ рдХреА рдЪреМрдбрд╝рд╛рдИ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рд▓реЗрдмрд▓ рд╣реИред рдпрд╣рд╛рдБ 2.x рдбреЗрдореЛ рд╕реЗ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ:

charts 2

рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЪрд╛рд░реНрдЯ 3 рдореЗрдВ, рдЕрдХреНрд╖ рдкрд░ рджрд┐рдЦрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреА рдЧрдгрдирд╛ рдХрд░рддреЗ рд╕рдордп рд▓реЗрдмрд▓рд╡рд┐рдбреНрде рдХреА рдЕрд╡рд╣реЗрд▓рдирд╛ рдХреА рдЬрд╛рддреА рд╣реИ (рдХрдВрдкреНрдпреВрдЯрдПрдХреНрд╕рд┐рд╕ рдЗрд╕ рдкрд░ рднреА рд╡рд┐рдЪрд╛рд░ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ)ред 10 (рд▓рдЧрднрдЧ) рд╡рд░реНрдгреЛрдВ рд╕реЗ рдКрдкрд░ рдХреА рдХрд┐рд╕реА рднреА рдЪреАрдЬрд╝ рдХреЛ рдЕрдВрддрддрдГ рдУрд╡рд░рд▓реИрдк рд╣реЛрдиреЗ рдХрд╛ рдореМрдХрд╛ рдорд┐рд▓рддрд╛ рд╣реИред рдпрд╣рд╛рдБ рд╡рд╣реА рдЙрджрд╛рд╣рд░рдг рд╣реИ, рдЪрд╛рд░реНрдЯ 3 рдбреЗрдореЛ рд╕реЗ:

charts 3

рдпрд╣ рд╣рдорд╛рд░реЗ рд▓рд┐рдП 2x рд╕реЗ рдПрдХ рдкреНрд░рдореБрдЦ рдкреНрд░рддрд┐рдЧрдорди рд╣реИред рдореИрдВрдиреЗ рдХреЛрдб рдХреЛ рджреЗрдЦрд╛ рд╣реИ рдФрд░ рдпрд╣рд╛рдВ рдореИрдВрдиреЗ рдЬреЛ рдкрд╛рдпрд╛ рд╣реИ:

XAxisRender.computeAxisValues тАЛтАЛтАЛтАЛрдкрд╣рд▓реЗ super.computeAxisValues тАЛтАЛтАЛтАЛрдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рдлрд┐рд░ рдХрдВрдкреНрдпреВрдЯрд╕рд╛рдЗрдЬ рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИред рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, рдХрдВрдкреНрдпреВрдЯрд╕рд╛рдЗрдЬрд╝ рдХрд╛ рдкрд░рд┐рдгрд╛рдо рдХрдВрдкреНрдпреВрдЯрдПрдХреНрд╕рд┐рд╕рд╡реИрд▓реНрдпреВрдЬрд╝ рдХреЗ рд▓рд┐рдП рдПрдХ рдЗрдирдкреБрдЯ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ рдЗрд╕реЗ рдкрд╣рд▓реЗ рдХреЙрд▓ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред

рд╣рд╛рд▓рд╛рдВрдХрд┐ рд▓реЗрдмрд▓ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП AxisRendererBase.computeAxisValues тАЛтАЛтАЛтАЛрдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдирд╛ рдЗрд╕ рд╕рдордп рдореЗрд░реА рд╕рдордЭ рдХреЗ рд╕реНрддрд░ рд╕реЗ рдкрд░реЗ рд╣реИред

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рдореЗрд░реЗ рдкрд╛рд╕ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рд╕рдорд╛рдзрд╛рди рд╣реИред рдпрд╣ рдореВрд▓ рд░реВрдк рд╕реЗ рдЕрддрд┐рд╡реНрдпрд╛рдкреА рд▓реЗрдмрд▓ рдХреЗ рдкреНрд░рддрд┐рдкрд╛рджрди рдХреЛ рдЫреЛрдбрд╝ рджреЗрддрд╛ рд╣реИ рд▓реЗрдХрд┐рди рдЗрд╕реЗ рдмреЗрд╣рддрд░ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЕрдм рдФрд░ рдЕрдзрд┐рдХ рдЦрд╛рд▓реА рд╕реНрдерд╛рди рд╣реИ рдЬрд╣рд╛рдВ рд▓реЗрдмрд▓ рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рдерд╛ред рдлрд┐рд░ рднреА рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдЕрднреА рдХреЗ рд▓рд┐рдП рд╕реНрд╡реАрдХрд╛рд░реНрдп рд╣реИ рдЬрдм рддрдХ рдХрд┐ рдпрд╣ рдореБрджреНрджрд╛ рд╣рд▓ рдирд╣реАрдВ рд╣реЛ рдЬрд╛рддрд╛ ...

рдПрдХ рдирдпрд╛ рдХрд╕реНрдЯрдо XAxisRenderer рдмрдирд╛рдПрдБ:

import Foundation
import Charts
#if !os(OSX)
import UIKit
#endif

// The original XAxisRender can result in overlapping labels on the
// x-axis (see https://github.com/danielgindi/Charts/issues/1969 ).
// This x-axis renderer will check if labels overlap and ignore drawing
// labels that overlap the previously drawn label.
class NoOverlappingLabelsXAxisRenderer: XAxisRenderer {
    public var shouldDrawBoundingBoxes = false
    public var labelSpacing = CGFloat(4.0)

    // Keep track of the previous label's rect
    private var previousLabelRect: CGRect?

    override func renderAxisLabels(context: CGContext) {
        previousLabelRect = nil
        super.renderAxisLabels(context: context)
    }

    //swiftlint:disable function_parameter_count
    override func drawLabel(context: CGContext, formattedLabel: String, x: CGFloat, y: CGFloat, attributes: [String : NSObject], constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) {
        guard let axis = self.axis as? XAxis else { return }

        // determine label rect
        let labelRect = CGRect(x: x - (axis.labelWidth / 2), y: y, width: axis.labelWidth, height: axis.labelHeight)

        // check if this label overlaps the previous label
        if let previousLabelRect = previousLabelRect, labelRect.origin.x <= previousLabelRect.origin.x + previousLabelRect.size.width + labelSpacing {
            // yes, skip drawing this label
            self.previousLabelRect = nil
            return
        }

        // remember this label's rect
        self.previousLabelRect = labelRect

        // draw label
        super.drawLabel(context: context, formattedLabel: formattedLabel, x: x, y: y, attributes: attributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians)

        // draw label rect for debugging purposes
        if shouldDrawBoundingBoxes {
            #if !os(OSX)
            // draw rect
            UIGraphicsPushContext(context)
            context.setStrokeColor(UIColor.red.cgColor)
            context.setLineWidth(0.5)
            context.addRect(labelRect)
            context.drawPath(using: .stroke)
            UIGraphicsPopContext()

            // draw line
            UIGraphicsPushContext(context)
            context.move(to: CGPoint(x: x, y: y))
            context.addLine(to: CGPoint(x: x, y: y - 4))
            context.setLineWidth(0.5)
            context.strokePath()
            UIGraphicsPopContext()
            #endif
        }
    }
    //swiftlint:disable function_parameter_count
}

рдФрд░ рдЕрдкрдирд╛ рдЪрд╛рд░реНрдЯ рд╕реЗрдЯ рдХрд░рддреЗ рд╕рдордп рдЗрд╕реЗ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░реЗрдВ:

        // instantiate the chart
        let lineChartView = PALineChartView(frame: ...)

        ...

        // configure the x-axis
        let xAxis = lineChartView.xAxis
        ...

        // configure custom renderer
        let xAxisRenderer = NoOverlappingLabelsXAxisRenderer(viewPortHandler: lineChartView.viewPortHandler, xAxis: lineChartView.xAxis, transformer: lineChartView.xAxisRenderer.transformer)
        xAxisRenderer.shouldDrawBoundingBoxes = true // enable to debug label rects
        lineChartView.xAxisRenderer = xAxisRenderer

рд╕рднреА 11 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рдПрдХ рдФрд░ рдкреНрд░рддрд┐рдЧрдорди рдЬреЛ рдЗрд╕рд╕реЗ рдЬреБрдбрд╝рд╛ рд╣реБрдЖ рд╣реИ, рд╡рд╣ рдпрд╣ рд╣реИ рдХрд┐ рд▓реЗрдмрд▓ рд╕реНрдкреЗрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд▓рдЧрддрд╛ рд╣реИ, рднрд▓реЗ рд╣реА рдЙрдкрд▓рдмреНрдз рд╕реНрдерд╛рди рдХреА рдкрд░рд╡рд╛рд╣ рдХрд┐рдП рдмрд┐рдирд╛ред рдЖрдИрдкреИрдб рдкрд░, рдЪрд╛рд░реНрдЯреНрд╕ 2.x рдкрд░, рдлреНрд░реЗрдорд╡рд░реНрдХ рдХрдИ рдПрдХреНрд╕рдПрдХреНрд╕рд┐рд╕ рд▓реЗрдмрд▓ рджрд┐рдЦрд╛ рд░рд╣рд╛ рдерд╛ рдЬреЛ рд╕рдВрднрд╡рддрдГ рд╕реНрдХреНрд░реАрди рдкрд░ рдлрд┐рдЯ рд╣реЛ рд╕рдХрддреЗ рдереЗред рдЕрдм, рдЪрд╛рд░реНрдЯреНрд╕ 3.x рдХреЗ рд╕рд╛рде, рдлреНрд░реЗрдорд╡рд░реНрдХ рд╣рдореЗрд╢рд╛ 6 xAxis рд▓реЗрдмрд▓ рджрд┐рдЦрд╛рдПрдЧрд╛ред

рдореИрдВ рд╕рдордЭрддрд╛ рд╣реВрдВ рдХрд┐ рдореИрдВ xAxis рд▓реЗрдмрд▓реЛрдВ рдХреА рдПрдХ рдЗрд╖реНрдЯрддрдо рд░рд╛рд╢рд┐ рдХреА рдЧрдгрдирд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ рдФрд░ рд▓реЗрдмрд▓ рдХреЛ рд╕реНрд╡рдпрдВ рд╕реЗрдЯ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ рд▓реЗрдХрд┐рди рдЪрд╛рд░реНрдЯ 2.x рдиреЗ рдЗрд╕реЗ рдореЗрд░реЗ рд▓рд┐рдП рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рд╕рдВрднрд╛рд▓рд╛ред рдЧрдгрдирд╛ рдХрд░рдирд╛ рднреА рдЗрддрдирд╛ рдЖрд╕рд╛рди рдирд╣реАрдВ рд╣реИред

рдореЗрд░реЗ рдкрд╛рд╕ рдПрдХ рд╣реА рд╕рдорд╕реНрдпрд╛ рдереА рдФрд░ рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ рдПрдХреНрд╕ рдЕрдХреНрд╖ рдкрд░ axisMinimum = 0.0 рдЬреЛрдбрд╝рдиреЗ рд╕реЗ рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рддрдп рд╣реЛ рдЧрдпрд╛ред рдореЗрд░рд╛ рдХреЛрдИ рднреА рд▓реЗрдмрд▓ рдЕрдм рдУрд╡рд░рд▓реИрдк рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред

рд╕рдВрдкрд╛рджрд┐рдд рдХрд░реЗрдВ: рдЬрд▓реНрдж рд╣реА рдмрд╛рдд рдХреА, рдпрд╣ рдЕрднреА рднреА рд╣рдорд╛рд░реЗ рд▓рд┐рдП рднреА рдПрдХ рдореБрджреНрджрд╛ рд╣реИред рдореИрдВ xAxis.avoidFirstLastClippingEnabled = false рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕реНрдерд┐рддрд┐ рдХреЛ рдХреБрдЫ рд╣рдж рддрдХ рдХрдо рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдерд╛ред

рдирдорд╕реНрддреЗ

рд╡рд╣реА рдореБрджреНрджрд╛ :(

рдирдорд╕реНрддреЗ!

рдореЗрд░реЗ рдкрд╛рд╕ рдПрдХ рд╣реА рд╕рдорд╕реНрдпрд╛ рд╣реИ, рдХреНрдпрд╛ рдХреЛрдИ рд╕рдорд╛рдзрд╛рди рд╣реИ?

рдореИрдВ рдЙрд╕реА рдореБрджреНрджреЗ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдЬрд╣рд╛рдВ рдПрдХреНрд╕-рдЕрдХреНрд╖ рд▓реЗрдмрд▓ рдУрд╡рд░рд▓реИрдк рдХрд░рддреЗ рд╣реИрдВ (рдПрдХ рдХрд╕реНрдЯрдо IAxisValueFormatter рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ):

screen shot 2017-01-16 at 15 14 41

рдЬреИрд╕рд╛ рдХрд┐ рдкрд╣рд▓реЗ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рд▓реЗрдмрд▓ рдХреА рд╕рдВрдЦреНрдпрд╛ рдЫрд╣ рдХреЗ рд▓рд┐рдП рддрдп рдХреА

_rects_ рдХрд╛ рдкреНрд░рддрд┐рдкрд╛рджрди рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдЕрддрд┐рд╡реНрдпрд╛рдкреА рд▓реЗрдмрд▓ рджрд┐рдЦрд╛рддрд╛ рд╣реИ:

    internal class func drawMultilineText(context: CGContext, text: String, knownTextSize: CGSize, point: CGPoint, attributes: [String : AnyObject]?, constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat)
    {
        ...
        NSUIGraphicsPopContext()

        // draw rect
        #if !os(OSX)
            NSUIGraphicsPushContext(context)
            context.setStrokeColor(UIColor.red.cgColor)
            context.setLineWidth(0.5)
            context.addRect(rect)
            context.drawPath(using: .stroke)
            NSUIGraphicsPopContext()
        #endif
    }

screen shot 2017-01-20 at 12 49 40

xAxis рдХреЗ рд▓рд┐рдП labelCount рдХреЛ рем рд╕реЗ рел рдХреЗ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдорд╛рди рд╕реЗ рдмрджрд▓рддреЗ рд╕рдордп, рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЕрдкреЗрдХреНрд╖рд┐рдд рел рдХреЗ рдмрдЬрд╛рдп рдХреЗрд╡рд▓ рек рд▓реЗрдмрд▓ рдкреНрд░рджрд╛рди рдХрд░реЗрдЧрд╛ (рдиреАрдЪреЗ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рджреЗрдЦреЗрдВ)ред xAxis.entries рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреЛ рдбреАрдмрдЧ рдХрд░рдирд╛ рднреА рдХреЗрд╡рд▓ 4 рдорд╛рди рджрд┐рдЦрд╛рддрд╛ рд╣реИ: "x-axis entries: [30.0, 60.0, 90.0, 120.0]" ред рдЬрдм рдЖрдк рдЗрд╕реЗ 4 рдореЗрдВ рдмрджрд▓рддреЗ рд╣реИрдВ рддреЛ рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреЗрд╡рд▓ 3 рд▓реЗрдмрд▓, рд╡рдЧреИрд░рд╣ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░реЗрдЧрд╛ред

        let xAxis = lineChartView.xAxis
        ...
        xAxis.labelCount = 5

screen shot 2017-01-20 at 17 42 34

рдЕрджреНрдпрддрди : рдореИрдВрдиреЗ рдЕрднреА рджреЗрдЦрд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЛ xAxis.setLabelCount(5, force: true) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд▓реЗрдмрд▓ рдЧрдгрдирд╛ рд╕реЗрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдЬреЛ _does_ рдЕрдкреЗрдХреНрд╖рд╛ рдХреЗ рдЕрдиреБрд░реВрдк рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рдореБрдЭреЗ рдЖрд╢реНрдЪрд░реНрдп рд╣реЛрддрд╛ рд╣реИ рдХрд┐ рдпрджрд┐ рдЖрдкрдХреЛ рдЗрд╕ рдкрджреНрдзрддрд┐ (#2085 рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рддреЛ labelCount рд▓рд┐рдЦрдиреЗ рдпреЛрдЧреНрдп рдХреНрдпреЛрдВ рд╣реИ?

рддрд░реНрдХ рдХреЗ рдЯреБрдХрдбрд╝реЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдореИрдВрдиреЗ рдкрд╛рдпрд╛ рдХрд┐ рддреНрд░реБрдЯрд┐ рдкреНрд░рд╡рдг рд╣реИ getLongestLabel рдЬреЛ рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рд╡рд░реНрдгреЛрдВ рдХреЗ рд╕рд╛рде рдЕрдХреНрд╖ рд▓реЗрдмрд▓ рдХрд╛ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдорд╛рди рджреЗрддрд╛ рд╣реИред рдЧреИрд░-рдореЛрдиреЛрдкреЙрдЬрд╝ рдХрд┐рдП рдЧрдП рдлреЛрдВрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╕рдордп рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдПрдХ рдЧрд▓рдд рдзрд╛рд░рдгрд╛ рд╣реИред _less_ рд╡рд░реНрдгреЛрдВ рд╡рд╛рд▓рд╛ рдПрдХ рд▓реЗрдмрд▓ рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рд╡рд░реНрдг рд╡рд╛рд▓реЗ рд▓реЗрдмрд▓ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ _wider_ рдкреНрд░рд╕реНрддреБрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдФрд░ рд▓реЗрдмрд▓ рдХреА рдЪреМрдбрд╝рд╛рдИ рдХреЛ рдбреАрдмрдЧ рдХрд░рдиреЗ рд╕реЗ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╣реЛ рд░рд╣рд╛ рд╣реИ: рд╡рд░реНрдгреЛрдВ рдХреА рдЙрдЪреНрдЪрддрдо рд╕рдВрдЦреНрдпрд╛ рд╡рд╛рд▓реЗ рд▓реЗрдмрд▓ рдХреА рдЪреМрдбрд╝рд╛рдИ 5 рдкрд┐рдХреНрд╕реЗрд▓ _less_ рдПрдХ рд▓реЗрдмрд▓ рд╕реЗ рдЕрдзрд┐рдХ рдереА рдХрдо рд╡рд░реНрдг рдереЗ, рд▓реЗрдХрд┐рди рд╡реНрдпрд╛рдкрдХ рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВред рд╕рднреА рдЕрд╡рд╕рд░реЛрдВ рдореЗрдВ рдЬрд╣рд╛рдВ рдпрд╣ рдХреЛрдб рдХрд╣рд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИ, рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреЗрд╡рд▓ рдмрд╛рдж рдореЗрдВ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреА рдЪреМрдбрд╝рд╛рдИ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП, рдПрдХ рдЕрдзрд┐рдХ рд╕рдЯреАрдХ (рд╣рд╛рд▓рд╛рдВрдХрд┐ рд╕рдВрднрд╡рддрдГ рдЕрдзрд┐рдХ рд╕рдВрд╕рд╛рдзрди рдЧрд╣рди) рд╕рдорд╛рдзрд╛рди рдЙрд╕ рддрд░реНрдХ рдХреЛ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдмрджрд▓рдирд╛ рд╣реЛрдЧрд╛:

    // The length of a label depends on its font (if it is not
    // a monospaced font) and rendering, not on the number or 
    // characters in a string.
    open func getLongestLabelSize() -> CGSize {
        var longestSize = CGSize()

        // iterate over all labels
        for i in 0 ..< entries.count {
            let text = getFormattedLabel(i)
            let size = text.size(attributes: [NSFontAttributeName: labelFont])

            if size.width > longestSize.width {
                longestSize = size
            }
        }

        return longestSize
    }

рдпрд╛ рдЗрд╕рд╕реЗ рднреА рдЬреНрдпрд╛рджрд╛ рд╕реНрд╡рд┐рдлреНрдЯреА рдЗрд╕ рддрд░рд╣:

    open func getLongestLabelSize() -> CGSize {
        return entries.enumerated().map { index, _ in
            return getFormattedLabel(index).size(attributes: [NSFontAttributeName: labelFont])
        }
        .sorted(by: { s1, s2 in return s1.width < s2.width })
        .last!
    }

рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ рдореБрдЭреЗ рдЕрднреА рддрдХ рдУрд╡рд░рд▓реИрдкрд┐рдВрдЧ рд▓реЗрдмрд▓ рдХрд╛ рдореВрд▓ рдХрд╛рд░рдг рдирд╣реАрдВ рдорд┐рд▓рд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рд╕реНрдкрд╖реНрдЯ рд╣реИ рдХрд┐ рд▓реЗрдмрд▓ рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рддрд░реНрдХ рджреЛрд╖рдкреВрд░реНрдг рд╣реИ ...

рдирдорд╕реНрддреЗ!
рдореЗрд░реЗ рд╕рд╛рде рднреА рд╡рд╣реА рджрд┐рдХреНрдХрдд рд╣реИред

рдореЗрд░реЗ рдкрд╛рд╕ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рд╕рдорд╛рдзрд╛рди рд╣реИред рдпрд╣ рдореВрд▓ рд░реВрдк рд╕реЗ рдЕрддрд┐рд╡реНрдпрд╛рдкреА рд▓реЗрдмрд▓ рдХреЗ рдкреНрд░рддрд┐рдкрд╛рджрди рдХреЛ рдЫреЛрдбрд╝ рджреЗрддрд╛ рд╣реИ рд▓реЗрдХрд┐рди рдЗрд╕реЗ рдмреЗрд╣рддрд░ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЕрдм рдФрд░ рдЕрдзрд┐рдХ рдЦрд╛рд▓реА рд╕реНрдерд╛рди рд╣реИ рдЬрд╣рд╛рдВ рд▓реЗрдмрд▓ рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рдерд╛ред рдлрд┐рд░ рднреА рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдЕрднреА рдХреЗ рд▓рд┐рдП рд╕реНрд╡реАрдХрд╛рд░реНрдп рд╣реИ рдЬрдм рддрдХ рдХрд┐ рдпрд╣ рдореБрджреНрджрд╛ рд╣рд▓ рдирд╣реАрдВ рд╣реЛ рдЬрд╛рддрд╛ ...

рдПрдХ рдирдпрд╛ рдХрд╕реНрдЯрдо XAxisRenderer рдмрдирд╛рдПрдБ:

import Foundation
import Charts
#if !os(OSX)
import UIKit
#endif

// The original XAxisRender can result in overlapping labels on the
// x-axis (see https://github.com/danielgindi/Charts/issues/1969 ).
// This x-axis renderer will check if labels overlap and ignore drawing
// labels that overlap the previously drawn label.
class NoOverlappingLabelsXAxisRenderer: XAxisRenderer {
    public var shouldDrawBoundingBoxes = false
    public var labelSpacing = CGFloat(4.0)

    // Keep track of the previous label's rect
    private var previousLabelRect: CGRect?

    override func renderAxisLabels(context: CGContext) {
        previousLabelRect = nil
        super.renderAxisLabels(context: context)
    }

    //swiftlint:disable function_parameter_count
    override func drawLabel(context: CGContext, formattedLabel: String, x: CGFloat, y: CGFloat, attributes: [String : NSObject], constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) {
        guard let axis = self.axis as? XAxis else { return }

        // determine label rect
        let labelRect = CGRect(x: x - (axis.labelWidth / 2), y: y, width: axis.labelWidth, height: axis.labelHeight)

        // check if this label overlaps the previous label
        if let previousLabelRect = previousLabelRect, labelRect.origin.x <= previousLabelRect.origin.x + previousLabelRect.size.width + labelSpacing {
            // yes, skip drawing this label
            self.previousLabelRect = nil
            return
        }

        // remember this label's rect
        self.previousLabelRect = labelRect

        // draw label
        super.drawLabel(context: context, formattedLabel: formattedLabel, x: x, y: y, attributes: attributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians)

        // draw label rect for debugging purposes
        if shouldDrawBoundingBoxes {
            #if !os(OSX)
            // draw rect
            UIGraphicsPushContext(context)
            context.setStrokeColor(UIColor.red.cgColor)
            context.setLineWidth(0.5)
            context.addRect(labelRect)
            context.drawPath(using: .stroke)
            UIGraphicsPopContext()

            // draw line
            UIGraphicsPushContext(context)
            context.move(to: CGPoint(x: x, y: y))
            context.addLine(to: CGPoint(x: x, y: y - 4))
            context.setLineWidth(0.5)
            context.strokePath()
            UIGraphicsPopContext()
            #endif
        }
    }
    //swiftlint:disable function_parameter_count
}

рдФрд░ рдЕрдкрдирд╛ рдЪрд╛рд░реНрдЯ рд╕реЗрдЯ рдХрд░рддреЗ рд╕рдордп рдЗрд╕реЗ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░реЗрдВ:

        // instantiate the chart
        let lineChartView = PALineChartView(frame: ...)

        ...

        // configure the x-axis
        let xAxis = lineChartView.xAxis
        ...

        // configure custom renderer
        let xAxisRenderer = NoOverlappingLabelsXAxisRenderer(viewPortHandler: lineChartView.viewPortHandler, xAxis: lineChartView.xAxis, transformer: lineChartView.xAxisRenderer.transformer)
        xAxisRenderer.shouldDrawBoundingBoxes = true // enable to debug label rects
        lineChartView.xAxisRenderer = xAxisRenderer

рдкреНрд░рд┐рдп 4рдПрдирдкреА,

рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рдареАрдХ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рдЖрдкрдХрд╛ рдмрд╣реБрдд рдмрд╣реБрдд рдзрдиреНрдпрд╡рд╛рдж!

рдореИрдВрдиреЗ рдЗрд╕ рд▓рд╛рдЗрди рдХреЛ рд╣рдЯрд╛ рджрд┐рдпрд╛, рдХреНрдпреЛрдВрдХрд┐ рдХреБрдЫ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рдЖрдЧреЗ рдХреЗ рдкрдбрд╝реЛрд╕рд┐рдпреЛрдВ рдХреЛ рднреА рдУрд╡рд░рд▓реИрдк рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛:
self.previousLabelRect = nil

рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, рдпрд╣ рд╕рдорд╛рдзрд╛рди рд╕рд╣реА рдирд╣реАрдВ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЕрдзрд┐рдХрд╛рдВрд╢ рдмрд╛рд░ рдЖрдк рд╣рдореЗрд╢рд╛ рдкрд╣рд▓реЗ рдФрд░ рдЕрдВрддрд┐рдо рдЖрдЗрдЯрдо рдХреЛ рдЕрдХреНрд╖ рдкрд░ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рдПрдХ рд╕рдВрднрд╛рд╡рд┐рдд рдЙрдкрд╛рдп рдпрд╣ рд╣реИ рдХрд┐ рдмрд╛рд░реА-рдмрд╛рд░реА рд╕реЗ рдмрд╛рдПрдВ рдФрд░ рджрд╛рдПрдВ (рджреЛ рдлреНрд░реЗрдо рд░рдЦрддреЗ рд╣реБрдП) рд╕реЗ рд▓реЗрдмрд▓ рдЬреЛрдбрд╝реЗрдВред

рдЪрд╛рд░реНрдЯ рд▓рд┐рдм рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд▓рд╛рдЗрди рдЪрд╛рд░реНрдЯ рдореЗрдВ рдорд▓реНрдЯреАрд▓рд╛рдЗрди рд▓реЗрдмрд▓ рдХреИрд╕реЗ рдмрдирд╛рдПрдВред x aixis рд▓реЗрдмрд▓ рдХрд╛ (рдЙрджреНрджреЗрд╢реНрдп c)
рдореЗрд░реЗ рдкрд╛рд╕ рджрд┐рдирд╛рдВрдХ рдФрд░ рд╕рдордп рдорд╛рди рд╣реИред рдореБрдЭреЗ рдПрдХ рд▓реЗрдмрд▓ рдореЗрдВ рджреЛрдиреЛрдВ рдорд╛рди рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдирд╛ рд╣реИред

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

newbiebie picture newbiebie  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

guanyanlin picture guanyanlin  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

anhltse03448 picture anhltse03448  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

heumn picture heumn  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

brytnvmg picture brytnvmg  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ