iOS uses UIBezierPath to realize unequal distance curve

Keywords: Mobile iOS

iOS, there are many good third parties for drawing lines, such as Charts, ECharts, etc., but I didn't find the ones with unequal distance, so I simply implemented them myself. First of all, the effect It's not too hard to draw lines and animations. I have customized a LineChartView and several models. The specific demo will be linked below

Several properties and methods are exposed for lineChartview, with comments Initialization configuration in the controller setChartView method

self.chartView.y_TextFont = [UIFont systemFontOfSize:14];
    self.chartView.minValue = 0;
    self.chartView.maxValue = 100;
    NSArray *x_names = @[@"sober",@"commonly",@"gold"];
    NSArray *xValue = @[@0,@50,@100];
    NSArray *x_colors = @[[UIColor redColor],[UIColor orangeColor],[UIColor yellowColor]];
    NSMutableArray *xAxis = [NSMutableArray new];
    for (int i = 0; i < x_names.count; i++) {
        XJYAxisModel * model = [XJYAxisModel new];
        model.clolor = x_colors[i];
        model.value = xValue[i];
        model.title = x_names[i];
        [xAxis addObject:model];
    }
    [self.chartView drawLineChartViewWithX_Value_Names:xAxis xCount:xCount];

I defined a method setXAxis in the controller to set the specific implementation of the model on the x-axis

- (NSArray *)setXAxis{
//    Maximum and minimum values - > values on each axis, such as the maximum value of 90, the minimum value of 0, 10 axes (9 gaps), the spacing of each axis is 10 (0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
    float min = 0;
    float max = 90;
    float space = (max - min)/(xCount - 1);
    NSMutableArray *xAxisArr = [NSMutableArray new];
    for (int i = 0 ; i < xCount; i++) {
        XJXAxisModel *model  = [XJXAxisModel new];
        model.value = [NSNumber numberWithFloat: i * space];
        model.title = [NSString stringWithFormat:@"12:0%d",i];
        model.clolor = [UIColor whiteColor];
        model.textFont = [UIFont systemFontOfSize:10];
        [xAxisArr addObject:model];
    }
    
    return xAxisArr;
}

There's a button on the page that triggers the assignment,

- (void)refreshData{
    static int a = 0;
    if (a == 0) {
        NSMutableArray *datas = [NSMutableArray new];
            NSArray *valueXs = @[@0,@5,@11,@19,@25,@31,@39,@43,@51,@59,@70,@85,@90];
            NSArray *valueYs = @[@0,@10,@55,@99,@88,@99,@77,@87,@10,@53,@80,@10,@0];
            for (int i = 0; i < valueXs.count; i++) {
                XJDataModel *model = [XJDataModel new];
                model.xValue = valueXs[i];
                model.yValue = valueYs[i];
                [datas addObject:model];
            }
        [self.chartView drawLineChartViewWithDataModels:datas withXAxisData:[self setXAxis]];
        a = 1;
    }else{
        NSMutableArray *datas = [NSMutableArray new];
            NSArray *valueXs = @[@0,@5,@11,@19,@25,@31,@39,@43,@51,@59,@70,@85,@90];
            NSArray *valueYs = @[@0,@90,@55,@9,@88,@19,@77,@87,@10,@93,@80,@10,@0];
            for (int i = 0; i < valueXs.count; i++) {
                XJDataModel *model = [XJDataModel new];
                model.xValue = valueXs[i];
                model.yValue = valueYs[i];
                [datas addObject:model];
            }
        [self.chartView drawLineChartViewWithDataModels:datas withXAxisData:[self setXAxis]];
        a = 0;
    }
}

In the specific implementation of line drawing, first assign x-axis copy, then draw dot line and set animation effect

- (void)drawLineChartViewWithDataModels:(NSArray<XJDataModel *> *)datas withXAxisData:(NSArray< XJXAxisModel * >*)xAxis{
    [self reset];
//    1. Set x-axis copy
    [self setXAxisData:xAxis];
    if (datas.count == 0) {
        return;
    }
//    [shapeLayer removeFromSuperlayer];
    //2. Get the coordinates of the target value point
    NSMutableArray *allPoints = [NSMutableArray array];
    for (int i = 0; i < datas.count; i++) {
        XJDataModel *model = [datas objectAtIndex:i];
        float Y = y_start - scaleY * model.yValue.floatValue;
        float X = x_start + scaleX * model.xValue.floatValue;
        NSLog(@"X,Y = (%.2f,%.2f)",X,Y);
        CGPoint point = CGPointMake(X, Y);
        [allPoints addObject:[NSValue valueWithCGPoint:point]];
    }
    
//    Draw a line
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:[allPoints[0] CGPointValue]];
    CGPoint PrePonit;
    for (int i =0; i<allPoints.count; i++) {
        if (i==0) {
            PrePonit = [allPoints[0] CGPointValue];
        }else{
            CGPoint NowPoint = [allPoints[i] CGPointValue];
            [path addCurveToPoint:NowPoint controlPoint1:CGPointMake((PrePonit.x+NowPoint.x)/2, PrePonit.y) controlPoint2:CGPointMake((PrePonit.x+NowPoint.x)/2, NowPoint.y)]; //Cubic curve
            PrePonit = NowPoint;
        }
    }
    shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = path.CGPath;
    shapeLayer.lineWidth = 2.0;
    shapeLayer.strokeColor = [UIColor orangeColor].CGColor;
    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    shapeLayer.borderWidth = 3.0;
    [self.subviews[0].layer addSublayer:shapeLayer];
//    Add animation
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    animation.duration = 1.0;
    animation.fromValue = @0.0f;
    animation.toValue = @1.0f;
    [shapeLayer addAnimation:animation forKey:@"strokeEnd"];
    for (int i = 0; i < datas.count; i++) {
        CGPoint point =[allPoints[i] CGPointValue];
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(point.x-2.5, point.y-2.5, 5, 5) cornerRadius:5];
        CAShapeLayer *layer = [CAShapeLayer layer];
        layer.strokeColor = [UIColor whiteColor].CGColor;
        layer.fillColor = [UIColor whiteColor].CGColor;
        layer.path = path.CGPath;
        [self.subviews[0].layer addSublayer:layer];
        [pointShapeLayers addObject:layer];
    }
}

Here's a brief introduction, demo , demo also implements double X functions

Posted by m00ch0 on Mon, 25 May 2020 07:17:42 -0700