在Swift中绘制折线图,​​包含1000多个数据点

时间:2015-01-14 12:17:33

标签: xcode macos swift drawing linechart

所以我有自定义视图,我试图在那里绘制折线图。首先我绘制xGrid,yGrid和middleline。以下是我的功能:

var width: CGFloat = 400
var height: CGFloat = 200
var xLines: CGFloat = 20
var yLines: CGFloat = 10
let color = NSColor.blackColor()

func drawMiddleLine() {
    let middleHeight = height / 2
    var context = NSGraphicsContext.currentContext()?.CGContext
    CGContextSetStrokeColorWithColor(context, color.CGColor)
    CGContextSetLineWidth(context, 2)
    CGContextMoveToPoint(context, 0, middleHeight)
    CGContextAddLineToPoint(context, width, middleHeight)
    CGContextStrokePath(context)
}

func drawYGrid() {
    let space = height / yLines
    var context = NSGraphicsContext.currentContext()?.CGContext
    CGContextSetStrokeColorWithColor(context, color.CGColor)
    for index in 0...Int(yLines) {
        CGContextMoveToPoint(context, width, (CGFloat(index) * space))
        CGContextAddLineToPoint(context, 0, (CGFloat(index) * space))
    }
    CGContextStrokePath(context)
}

func drawXGrid() {
    let space = width / xLines
    var context = NSGraphicsContext.currentContext()?.CGContext
    CGContextSetStrokeColorWithColor(context, color.CGColor)
    for index in 0...Int(xLines) {
        CGContextMoveToPoint(context, (CGFloat(index) * space), self.bounds.height)
        CGContextAddLineToPoint(context, CGFloat(index) * space, 0)
    }
    CGContextStrokePath(context)
}

现在我有基本网格,宽度是高度的两倍。现在我想缩放我的y轴,所以我要扩展我的数据(在这个例子中,我只取最大正数):

func getMaxValue(data: Array<CGFloat>) ->CGFloat {
    let max = maxElement(data)
    return max
}

现在我缩放我的y轴:

func scaleYAxis(data: Array<CGFloat>) ->Array<CGFloat> {
    let maxValue = getMaxValue(data)
    var factor = height / maxValue / 4
    var scaledY = data.map({datum -> CGFloat in
        var newValue = datum * factor
        return newValue
        })
    return scaledY
}

但是当我用太多的数据点绘制我的线条时,我的绘图会搞砸,因为数据点太多了。当有大约50个数据点时,它没问题。

例如,我想在结果中得到类似的内容:JavaScript linechart with many many datapoints

我有什么想法可以设法在Swift中获得它?

我的绘图方法是这样的:

func drawLine(data: Array<CGFloat>) {
    var path = CGPathCreateMutable()
    var middleHeight = height / 2
    CGPathMoveToPoint(path, nil, 0, middleHeight)
    var scaledY = sclaleYAxis(data)

    for index in 0..<data.count {
        var xSpot = data[index]
        var ySpot = middleHeight + scaledY[index]

        CGPathAddLineToPoint(path, nil, xSpot, ySpot)
    }
    var layer = CAShapeLayer()
    layer.frame = self.bounds
    layer.path = path
    layer.strokeColor = NSColor.greenColor().CGColor
    layer.fillColor = nil
    layer.lineWidth = 3
    self.layer?.addSublayer(layer)

    // I Have lineLayerStore and I delete all lines when it is needed
    lineLayerStore.append(layer)
}

1 个答案:

答案 0 :(得分:1)

如果没有看到屏幕截图,我无法理解确切的问题,但数据点的可见性取决于屏幕上的像素数。

您不能显示比像素数更多的数据点,不允许某些数据点共享相同的像素列或下采样数据。

您有3个选项:

  1. 使X轴可滚动并将scrollview的contentSize宽度设置为 数字数据点。

  2. 保持X轴宽度固定,对数据进行下采样以减少数量 要显示的数据点,然后绘制关于的图表 下采样数据。

  3. 什么都不做,尝试在固定宽度内绘制所有数据点, 如果你的计算是正确的,一些数据点会重叠 在图表中共享相同的像素列。

  4. 解释编辑第3个选项:

    为了在200像素宽的X轴上显示1000个数据点,每个像素将有5个数据点。所以;

    数据点0,1,2,3和4将在第1个像素列上绘制。

    将在第二像素列上绘制数据点5,6,7,8和9。

    数据点10,11,12,13和14将绘制在第3像素列上。

    等等。

    对于您的示例,有400像素和1000个数据点,这意味着每像素2.5个数据点。所以;

    数据点0,1,2将在第1个像素列上绘制。

    数据点3,4将在第二个像素列上绘制。

    数据点5,6,7将在第3像素列上绘制。

    数据点8,9将在第4像素列上绘制。

    继续这样下去。