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
相关产品推荐
相关产品推荐

