How can I use a UIBezierPath to create a curved line chart with data points?

本小妞迷上赌 提交于 2020-06-28 05:16:32

问题


I am trying to use a UIBezierPath to create a line chart. My goal is to animate this chart and have it appear to be "wavy." But I can't seem to get the path to mirror my data.

Edit: Added Data Points

let dataPoints: [Double]


func wave(at elapsed: Double) -> UIBezierPath {
    let amplitude = CGFloat(50) - abs(fmod(CGFloat(elapsed/2), 3) - 1.5) * 100
    func f(_ x: Int) -> CGFloat {
        return sin(((CGFloat(dataPoints[x]*100) / bounds.width) + CGFloat(elapsed/2)) * 4 * .pi) * amplitude + bounds.midY
    }
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0, y: f(0)))
    for x in 1..<dataPoints.count {
        path.addLine(to: CGPoint(x: CGFloat(x), y: f(x)))
    }
    return path
}

回答1:


A few issues:

  1. You defined your f(_:), but aren’t using it, but rather using dataPoints (which I don’t see you populating anywhere). It’s probably easiest to just use your f function directly.

  2. You are adding cubic Bézier curves to your path, but your control points are the same data points. That will offer no smoothing. The control points really should be points before and after your x value, along the line tangent to the curve in question (e.g. the along the line defined by the data point and the slope, a.k.a. the first derivative, of your curve).

    It wouldn’t be so hard to do this, but the easy solution is to just use addLine. If you use enough data points, it will appear to be smooth.

  3. When you do this, it won’t be a nice smooth sine curve if using only 12 data points. Instead, use a much larger number (120 or 360 or whatever). For the sake of a single sine curve, don’t worry about the number of data points unless you start to see performance issues.

  4. Your x values in your CGPoint values are smaller than you probably intended. If you only go from 0..<12, the resulting path will be only 12 points wide; lol. Use x values to go all the way across the view in question.

So, you might end up with something like:

func wave(at elapsed: Double) -> UIBezierPath {
    let amplitude = CGFloat(50) - abs(fmod(CGFloat(elapsed/2), 3) - 1.5) * 40
    func f(_ x: Int) -> CGFloat {
        return sin(((CGFloat(x) / bounds.width) + CGFloat(elapsed/2)) * 4 * .pi) * amplitude + bounds.midY
    }
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0, y: f(0)))
    for x in 1...Int(bounds.width) {
        path.addLine(to: CGPoint(x: CGFloat(x), y: f(x)))
    }
    return path
}

Marry that with a CADisplayLink to update the curve, and that yields:



来源:https://stackoverflow.com/questions/60743134/how-can-i-use-a-uibezierpath-to-create-a-curved-line-chart-with-data-points

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!