如何在iOS中绘制平滑曲线的折线图?

Ric*_*ick 1 charts curve ios

我有一些像 (x1,y1), (x2,y2), (x3,y3)...

现在我想绘制一个曲线平滑的图表?

我正在尝试绘制如下

-(void)drawPrices
{
    NSInteger count = self.prices.count;
    
    UIBezierPath *path = [UIBezierPath bezierPath]; 
    path.lineCapStyle = kCGLineCapRound;

    for(int i=0; i<count-1; i++)
    {
        CGPoint controlPoint[2];
        
        CGPoint p = [self pointWithIndex:i inData:self.prices];
        if(i==0)
        {
            [path moveToPoint:p];
        }

        CGPoint nextPoint, previousPoint, m;
        nextPoint = [self pointWithIndex:i+1 inData:self.prices];
        previousPoint = [self pointWithIndex:i-1 inData:self.prices];
        
        if(i > 0) {
            m.x = (nextPoint.x - previousPoint.x) / 2;
            m.y = (nextPoint.y - previousPoint.y) / 2;
        } else {
            m.x = (nextPoint.x - p.x) / 2;
            m.y = (nextPoint.y - p.y) / 2;
        }
        
        controlPoint[0].x = p.x + m.x * 0.2;
        controlPoint[0].y = p.y + m.y * 0.2;
        
        // Second control point
        nextPoint = [self pointWithIndex:i+2 inData:self.prices];
        previousPoint = [self pointWithIndex:i inData:self.prices];
        p = [self pointWithIndex:i + 1 inData:self.prices];
        m = zeroPoint;
        
        if(i < self.prices.count - 2) {
            m.x = (nextPoint.x - previousPoint.x) / 2;
            m.y = (nextPoint.y - previousPoint.y) / 2;
        } else {
            m.x = (p.x - previousPoint.x) / 2;
            m.y = (p.y - previousPoint.y) / 2;
        }
        
        controlPoint[1].x = p.x - m.x * 0.2;
        controlPoint[1].y = p.y - m.y * 0.2;
        
        [path addCurveToPoint:p controlPoint1:controlPoint[0] controlPoint2:controlPoint[1]];
    }
    
    CAShapeLayer *lineLayer = [CAShapeLayer layer];
    lineLayer.path = path.CGPath;
    lineLayer.lineWidth = LINE_WIDTH;
    lineLayer.strokeColor = _priceColor.CGColor;
    lineLayer.fillColor = [UIColor clearColor].CGColor;

    [self.layer addSublayer:lineLayer];
}
Run Code Online (Sandbox Code Playgroud)

但在某些情况下,该线路会“返回”,例如

有没有更好的方法来做到这一点?

iSp*_*n17 11

我为您提供了另一个我曾经成功的解决方案。它需要 SpriteKit,它有一个很棒的工具,叫做SKKeyFrameSequence它能够进行样条插值,如Apple 提供的本教程所示。

所以我们的想法是,您将像这样创建正确的 SKKeyFrameSequence 对象(假设您的数据位于(CGFloat, CGFloat)(x, y) 元组的数组中):

let xValues = data.map { $0.0 }
let yValues = data.map { $0.1 }
let sequence = SKKeyFrameSequence(keyFrameValues: yValues,
                                  times: xValues.map { NSNumber($0) })
sequence.interpolationMode = .spline

let xMin = xValues.min()!
let xMax = xValues.max()!
Run Code Online (Sandbox Code Playgroud)

然后,如果您将插值样条分成 200 条(如果您愿意,可以更改此值,但对我而言,这会导致人眼看到平滑的波浪),您可以绘制一条由小线组成的路径。

var splinedValues = [(CGFloat, CGFloat)]()
stride(from: xMin, to: xMax, by: (xMax - xMin) / 200).forEach {
    splinedValues.append((CGFloat($0),
                          sequence.sample(atTime: CGFloat($0)) as! CGFloat))
}
Run Code Online (Sandbox Code Playgroud)

最后是路径(我将使用 SwiftUI,但您也可以以相同的方式使用 UIKit。):

Path { path in
    path.move(to: CGPoint(x: splinedValues[0].0, y: splinedValues[0].1))

    for splineValue in splinedValues.dropFirst() {
        path.addLine(to: CGPoint(x: splineValue.0, y: splineValue.1))
    }
}
Run Code Online (Sandbox Code Playgroud)

对于值

[(1, 4), (2, 6), (3, 7), (4, 5), (5, 3), (6, -1), (7, -2), (8, -2.5), (9, -2), (10, 0), (11, 4)]
Run Code Online (Sandbox Code Playgroud)

我用上面描述的方法得到了这样的图:(我也添加了点以更好地评估结果)

在此处输入图片说明