当前位置: 首页 > 工具软件 > ios-charts > 使用案例 >

iOS-Charts的x轴坐标间隔与坐标个数修改

杜诚
2023-12-01

项目需要用到数据曲线,而且曲线是实时变化的,所以选择了第三方图表库Charts。选择Charts的最主要原因还是它是作者按照Android的常用曲线库MPAndroidChart写的,功能UI基本相近。

曲线的基本用法很简单,在GitHub上可以下载到demo,有各种示例,K线图、折线图和饼状图等都有。以下主要讲我自己碰到的特定问题和解决方案。

  • 自定义的坐标文字和间隔

项目曲线是根据时间不停变化的,而坐标轴是一个点一个数据,也就是对应的一秒一个数据。但是项目需求坐标单位是分钟,所以每个坐标间就有60个数据。

ChartXAxis有两个属性:axisMaximum、labelCount

    /// The maximum value for this axis.
    /// If set, this value will not be calculated automatically depending on the provided data.
    /// Use `resetCustomAxisMax()` to undo this.
    /// the number of label entries the axis should have
    /// max = 25,
    /// min = 2,
    /// default = 6,
    /// be aware that this number is not fixed and can only be approximated

按照注释的意思,我只需要设置一个最大数值,然后再设置坐标显示个数,比如我设置

        xAxis.axisMaximum = 600;
        [xAxis setLabelCount:11];

这样就相当于坐标轴上显示从0到10的坐标,单位是分钟,一共有600秒的数据。然而曲线上实际只显示了6个数字,中间只有5个间隔。我百思不得其解,只能从源码入手。

  • AxisRendererBase.swift

打开AxisRendererBase.swift文件,找到computeAxisValues函数,代码如下:

@objc open func computeAxisValues(min: Double, max: Double)
    {
        guard let axis = self.axis else { return }
        
        let yMin = min
        let yMax = max
        
        let labelCount = axis.labelCount
        let range = abs(yMax - yMin)
        
        if labelCount == 0 || range <= 0 || range.isInfinite
        {
            axis.entries = [Double]()
            axis.centeredEntries = [Double]()
            return
        }
        
        // Find out how much spacing (in y value space) between axis values
        let rawInterval = range / Double(labelCount)
        var interval = rawInterval.roundedToNextSignficant()
        // If granularity is enabled, then do not allow the interval to go below specified granularity.
        // This is used to avoid repeated values when rounding values for display.
        if axis.granularityEnabled
        {
            interval = interval < axis.granularity ? axis.granularity : interval
        }
        
        // Normalize interval
        let intervalMagnitude = pow(10.0, Double(Int(log10(interval)))).roundedToNextSignficant()
        let intervalSigDigit = Int(interval / intervalMagnitude)
        if intervalSigDigit > 5
        {
            // Use one order of magnitude higher, to avoid intervals like 0.9 or 90
            interval = floor(10.0 * Double(intervalMagnitude))
        }
        
        var n = axis.centerAxisLabelsEnabled ? 1 : 0
        
        // force label count
        if axis.isForceLabelsEnabled
        {
            interval = Double(range) / Double(labelCount - 1)
            
            // Ensure stops contains at least n elements.
            axis.entries.removeAll(keepingCapacity: true)
            axis.entries.reserveCapacity(labelCount)
            
            var v = yMin
            
            for _ in 0 ..< labelCount
            {
                axis.entries.append(v)
                v += interval
            }
            
            n = labelCount
        }
        else
        {
            // no forced count
        
            var first = interval == 0.0 ? 0.0 : ceil(yMin / interval) * interval
            
            if axis.centerAxisLabelsEnabled
            {
                first -= interval
            }
            
            let last = interval == 0.0 ? 0.0 : (floor(yMax / interval) * interval).nextUp
            
            if interval != 0.0 && last != first
            {
                for _ in stride(from: first, through: last, by: interval)
                {
                    n += 1
                }
            }
            else if last == first && n == 0
            {
                n = 1
            }

            // Ensure stops contains at least n elements.
            axis.entries.removeAll(keepingCapacity: true)
            axis.entries.reserveCapacity(labelCount)
            
            var f = first
            var i = 0
            while i < n
            {
                if f == 0.0
                {
                    // Fix for IEEE negative zero case (Where value == -0.0, and 0.0 == -0.0)
                    f = 0.0
                }
                
                axis.entries.append(Double(f))
                
                f += interval
                i += 1
            }
        }
        
        // set decimals
        if interval < 1
        {
            axis.decimals = Int(ceil(-log10(interval)))
        }
        else
        {
            axis.decimals = 0
        }
        
        if axis.centerAxisLabelsEnabled
        {
            axis.centeredEntries.reserveCapacity(n)
            axis.centeredEntries.removeAll()
            
            let offset: Double = interval / 2.0
            
            for i in 0 ..< n
            {
                axis.centeredEntries.append(axis.entries[i] + offset)
            }
        }
    }

可以看到这几句代码

        let intervalMagnitude = pow(10.0, Double(Int(log10(interval)))).roundedToNextSignficant()
        let intervalSigDigit = Int(interval / intervalMagnitude)
        if intervalSigDigit > 5
        {
            // Use one order of magnitude higher, to avoid intervals like 0.9 or 90
            interval = floor(10.0 * Double(intervalMagnitude))
        }

这下找到罪魁祸首了,原来在这里对labelCount还做了处理,找到了原因解决起来就不难了,只有重写这个函数,将labelCount保持设置的值,就可以显示正常的x坐标轴了。

 类似资料: