You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

danielgindi/Charts获取X轴标签位置及跨模块值传递问题

解决X轴标签位置无法正确传递到项目中的问题

问题分析

核心问题是XAxisPosition类的存储逻辑不匹配标签位置的批量生成特性,且实例化方式导致数据被重置为初始值。

解决方案步骤

1. 重构XAxisPosition类

将单个值存储改为数组,适配多个X轴标签的位置存储需求:

@objc open class XAxisPosition: NSObject {
    // 存储所有可见X轴标签的X坐标
    @objc open var labelPositions: [CGFloat] = []
}

2. 自定义XAxisRenderer子类

在子类中持有XAxisPosition实例,并重写drawLabels方法,实时收集每个标签的位置:

@objc open class CustomXAxisRenderer: XAxisRenderer {
    // 持有位置存储实例,避免全局初始化重置
    @objc open let axisPosition = XAxisPosition()
    
    override open func drawLabels(context: CGContext, pos: CGFloat, anchor: CGPoint) {
        super.drawLabels(context: context, pos: pos, anchor: anchor)
        
        // 每次绘制前清空旧数据,避免累积
        axisPosition.labelPositions.removeAll()
        
        guard let transformer = self.transformer else { return }
        
        let paraStyle = ParagraphStyle.default.mutableCopy() as! MutableParagraphStyle
        paraStyle.alignment = .center
        
        let labelAttrs: [NSAttributedString.Key : Any] = [.font: axis.labelFont,
                                                         .foregroundColor: axis.labelTextColor,
                                                         .paragraphStyle: paraStyle]

        let labelRotationAngleRadians = axis.labelRotationAngle.DEG2RAD
        let isCenteringEnabled = axis.isCenterAxisLabelsEnabled
        let valueToPixelMatrix = transformer.valueToPixelMatrix

        var position = CGPoint.zero
        var labelMaxSize = CGSize.zero
        
        if axis.isWordWrapEnabled
        {
            labelMaxSize.width = axis.wordWrapWidthPercent * valueToPixelMatrix.a
        }
        
        let entries = axis.entries
        
        for i in entries.indices
        {
            let px = isCenteringEnabled ? CGFloat(axis.centeredEntries[i]) : CGFloat(entries[i])
            position = CGPoint(x: px, y: 0)
                .applying(valueToPixelMatrix)

            guard viewPortHandler.isInBoundsX(position.x) else { continue }
            
            let label = axis.valueFormatter?.stringForValue(axis.entries[i], axis: axis) ?? ""
            let labelns = label as NSString
            
            if axis.isAvoidFirstLastClippingEnabled
            {
                if i == axis.entryCount - 1 && axis.entryCount > 1
                {
                    let width = labelns.boundingRect(with: labelMaxSize, options: .usesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width
                    
                    if width > viewPortHandler.offsetRight * 2.0,
                        position.x + width > viewPortHandler.chartWidth
                    {
                        position.x -= width / 2.0
                    }
                }
                else if i == 0
                {
                    let width = labelns.boundingRect(with: labelMaxSize, options: .usesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width
                    position.x += width / 2.0
                }
            }
            
            // 将当前标签的X坐标存入数组
            axisPosition.labelPositions.append(position.x)
            
            drawLabel(context: context,
                      formattedLabel: label,
                      x: position.x,
                      y: pos,
                      attributes: labelAttrs,
                      constrainedTo: labelMaxSize,
                      anchor: anchor,
                      angleRadians: labelRotationAngleRadians)
        }
    }
}

3. 在项目中获取实时位置数据

在图表初始化时设置自定义Renderer,并在图表绘制完成后读取位置:

// 初始化图表时替换默认X轴Renderer
let customXRenderer = CustomXAxisRenderer(
    viewPortHandler: chartView.viewPortHandler,
    axis: chartView.xAxis,
    transformer: chartView.getTransformer(forAxis: .left)
)
chartView.xAxisRenderer = customXRenderer

// 触发图表绘制
chartView.notifyDataSetChanged()

// 在图表绘制完成后(可通过ChartViewDelegate的chartDidDraw方法)获取位置
let labelPositions = customXRenderer.axisPosition.labelPositions
// 使用labelPositions数组中的坐标值创建自定义X轴

关键注意点

  • 避免使用全局/静态XAxisPosition实例:全局实例会在模块导入时重新初始化,导致数据被重置为0;
  • 每次绘制前清空数组:确保数据是当前图表状态下的最新标签位置;
  • 通过Renderer实例访问数据:直接从自定义Renderer的axisPosition属性读取,保证拿到的是实时计算后的位置。

内容的提问来源于stack exchange,提问作者eeuin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 04:25:17