Time-sharing diagram of IOS-side K-line series - overall construction

K-line series catalog

To view the catalog click here

submit questions

I remember there was a brain teaser:

Q: How to put elephants in the refrigerator?
Answer: There are three steps in total (1) Open the refrigerator door (2) Push the elephant in (3) Close the refrigerator door

Although it is a joke, it is also a way of solving problems from another perspective. The 2008 Beijing Olympic Games, as a large-scale event, lasted for 8 years, but the process of the project was only divided into five process groups:

(1) Initiation process group (2) Planning process group (3) Execution process group (4) Monitoring process group (5) Closing process group

For our theme: time-sharing charts, we can actually look at it this way. How to draw a time-sharing diagram, if you have read the previous articles (click here if you haven't ), you should have a general process in your mind:

  1. Draw the border of the time-sharing graph
  2. Draw the X-axis time points of the time-sharing graph
  3. Draw price range markers
  4. draw time division
  5. draw the moving average
  6. draw breathing lamp

After completing 6 steps, the time-sharing diagram has been drawn.

start

When you have an idea, start working! Although I have mentioned some drawing methods in the previous articles, I will say it again here, and it should be reviewed.

(1) Draw the border of the time-sharing diagram

The effect to be achieved is a 6 * 4 square border, as shown below:

Time-sharing chart border

That is to say, we divide into two for loops to complete the drawing of 7 horizontal lines and 5 vertical lines. Code on!

    CGRect rect = CGRectMake(frameX, frameY, frameW, frameH);
    UIBezierPath *framePath = [UIBezierPath bezierPathWithRect:rect];
    CAShapeLayer *layer = [CAShapeLayer layer];

    float unitW = frameW/6;
    float unitH = frameH/4;
    //绘制7条竖线
    for (int idx=0; idx<7; idx++)
    {
        CGPoint startPoint = CGPointMake(frameX + unitW * idx, frameY);
        CGPoint endPoint   = CGPointMake(frameX + unitW * idx, frameY + frameH);
        [framePath moveToPoint:startPoint];
        [framePath addLineToPoint:endPoint];
    }
    //绘制5条横线
    for (int idx=0; idx<5; idx++)
    {
        CGPoint startPoint = CGPointMake(frameX, frameY + unitH * idx);
        CGPoint endPoint   = CGPointMake(frameX + frameW, frameY + unitH * idx);
        [framePath moveToPoint:startPoint];
        [framePath addLineToPoint:endPoint];
    }
    //设置图层的属性
    layer.path = framePath.CGPath;
    layer.lineWidth = 0.5f;
    layer.strokeColor = [UIColor colorWithRed:220.f/255.f green:220.f/255.f blue:220.f/255.f alpha:1.f].CGColor;
    layer.fillColor = [UIColor clearColor].CGColor;

(2) Draw the X-axis time point of the time-sharing graph

After the frame is drawn, start to draw the time point at the bottom of the frame. Since the default trading time of the spot K-line frame is 24 hours, the time is also set to 6.01 to 6.00. In this case, a square every 4 hours, a total of 7 time points need to be drawn. code show as below:

    //坐标点数组
    NSArray *timePointArr = @[@"06:01", @"10:00", @"14:00", @"18:00", @"22:00", @"02:00", @"06:00"];
    NSDictionary *attribute = @{NSFontAttributeName:[UIFont systemFontOfSize:9.f]};
    CGRect strRect = [self rectOfNSString:@"00:00" attribute:attribute];
    float strW = CGRectGetWidth(strRect);
    float strH = CGRectGetHeight(strRect);

    float unitW = CGRectGetWidth(self.frame) / 6;
    //循环绘制坐标点
    for (int idx = 0; idx < timePointArr.count; idx++)
    {
        CATextLayer *textLayer = nil;

        if (idx == timePointArr.count-1)
        {//最后一个
            CGRect rect = CGRectMake(idx * unitW - strW, CGRectGetHeight(self.frame)-timePointH, strW, strH);
            textLayer = [CATextLayer getTextLayerWithString:timePointArr[idx] textColor:[UIColor blackColor] fontSize:9.f backgroundColor:[UIColor clearColor] frame:rect];
        }else if(idx == 0)
        {//第一个
            CGRect rect = CGRectMake(idx * unitW, CGRectGetHeight(self.frame)-timePointH, strW, strH);
            textLayer = [CATextLayer getTextLayerWithString:timePointArr[idx] textColor:[UIColor blackColor] fontSize:9.f backgroundColor:[UIColor clearColor] frame:rect];
        }else
        {//中间
            CGRect rect = CGRectMake(idx * unitW - strW/2, CGRectGetHeight(self.frame)-timePointH, strW, strH);
            textLayer = [CATextLayer getTextLayerWithString:timePointArr[idx] textColor:[UIColor blackColor] fontSize:9.f backgroundColor:[UIColor clearColor] frame:rect];
        }

        [self.layer addSublayer:textLayer];
    }

(3) Draw the price range mark

By now, the border and time points are drawn. Next, draw the price range. The method of finding the price range is to first obtain the limit value, that is, the maximum and minimum values, and then obtain them according to the following rules:

if(ABS(Maximum value in the current time division line - yesterday's closing price)) >= (ABS(Yesterday's closing price - the minimum value in the current time division line))
{
The uppermost price = the maximum value in the current time division line;
the lowest Side price = yesterday's closing price - ABS (the maximum value in the current time division line - yesterday's closing price);
}else
{
top price = yesterday's closing price + ABS (yesterday's closing price - the minimum value in the current time division line);
the bottom side price = minimum value in current ticks;
}

code show as below:

    //循环绘制5行数据
    //左边是价格  右边是百分比
    for (int idx = 0; idx < 5; idx++)
    {
        float height = 0.f;
        if (idx == 4)
        {
            height = idx * unitH - CGRectGetHeight(priceRect);
        } else
        {
            height = idx * unitH;
        }
        CGRect leftRect = CGRectMake(0,
                                     height,
                                     CGRectGetWidth(priceRect),
                                     CGRectGetHeight(priceRect));
        CGRect rightRect = CGRectMake(CGRectGetMaxX(self.frame)-CGRectGetWidth(perRect)-14,
                                      height,
                                      CGRectGetWidth(perRect),
                                      CGRectGetHeight(perRect));
        //计算价格和百分比
        NSString *leftStr = [NSString stringWithFormat:@"%.2f", self.maxValue - idx * unitPrice];
        NSString *rightStr = [NSString stringWithFormat:@"%.2f%%", (self.maxValue - idx * unitPrice - self.yc)/self.yc];

        CATextLayer *leftLayer = [CATextLayer getTextLayerWithString:leftStr
                                                           textColor:[UIColor blackColor]
                                                            fontSize:9.f
                                                     backgroundColor:[UIColor clearColor]
                                                               frame:leftRect];
        CATextLayer *rightLayer = [CATextLayer getTextLayerWithString:rightStr
                                                            textColor:[UIColor blackColor]
                                                             fontSize:9.f
                                                      backgroundColor:[UIColor clearColor]
                                                                frame:rightRect];

        [self.layer addSublayer:leftLayer];
        [self.layer addSublayer:rightLayer];
    }

(4) Draw the time-sharing line

After the left and right price ranges are drawn, the next step is to draw the time division line.

1. Pay attention here, because the default is 24 hours of trading time, the time division line is that each point is one minute, and the conversion of 24 hours into minutes is 1440 minutes.

2. Divide the width of the border by 1440 to get the width of each point, so that when converting the coordinates of each time-sharing point, the x value can be obtained using this width.

3. How to find the y value of each time point? It is to first subtract the minimum value from the maximum value to obtain the value corresponding to the height of the frame, and then divide this value by the height of the frame to obtain the height corresponding to the unit value. Then you can use this value directly when calculating the y value.

The conversion code is as follows:

    CGFloat unitW = CGRectGetWidth(self.frame) / 1440;
    CGFloat unitValue = (self.maxValue - self.minValue) / (CGRectGetHeight(self.frame) - timePointH);

    NSMutableArray *pointArr = [NSMutableArray array];
    //遍历数据模型
    [self.timeCharModelArr enumerateObjectsUsingBlock:^(YKTimeChartModel * _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {

        CGFloat x = idx * unitW;
        //生成分时线坐标点
        CGPoint linePoint = CGPointMake(x, ABS(CGRectGetMaxY(self.frame) - timePointH) - (model.clp - self.minValue)/ unitValue);
        //生成均线坐标点
        CGPoint avgPoint = CGPointMake(x, ABS(CGRectGetMaxY(self.frame) - timePointH) - (model.avp - self.minValue)/ unitValue);

        YKTimeLinePointModel *pointModel = [YKTimeLinePointModel new];
        pointModel.linePoint = linePoint;
        pointModel.avgPoint = avgPoint;
        [pointArr addObject:pointModel];
    }];

    return pointArr;

After the coordinates of the points of each time-sharing line are all converted, we can directly traverse the array to draw in a loop. Above code:

    //绘制分时线
    YKTimeLinePointModel *firstModel = pointArr.firstObject;
    [timeLinePath moveToPoint:firstModel.linePoint];
    for (int i=1; i<pointArr.count; i++)
    {
        YKTimeLinePointModel *model = pointArr[i];
        [timeLinePath addLineToPoint:model.linePoint];
    }
    lineLayer.path = timeLinePath.CGPath;
    lineLayer.lineWidth = 0.4f;
    lineLayer.strokeColor = [UIColor colorWithRed:100.f/255.f green:149.f/255.f blue:237.f/255.f alpha:1.f].CGColor;
    lineLayer.fillColor = [UIColor clearColor].CGColor;

    //绘制背景区域
    YKTimeLinePointModel *lastModel = [pointArr lastObject];
    [timeLinePath addLineToPoint:CGPointMake(lastModel.linePoint.x, CGRectGetHeight(self.frame) - timePointH)];
    [timeLinePath addLineToPoint:CGPointMake(firstModel.linePoint.x, CGRectGetHeight(self.frame)- timePointH)];
    fillLayer.path = timeLinePath.CGPath;
    fillLayer.fillColor = [UIColor colorWithRed:135.f/255.f green:206.f/255.f blue:250.f/255.f alpha:0.5f].CGColor;
    fillLayer.strokeColor = [UIColor clearColor].CGColor;
    fillLayer.zPosition -= 1;

(5) Draw the moving average

After the time division line is drawn, then draw the moving average, which is the yellow line:

    CAShapeLayer *avgLineLayer = [CAShapeLayer layer];

    UIBezierPath *avgLinePath = [UIBezierPath bezierPath];
    YKTimeLinePointModel *firstModel = pointArr.firstObject;
    [avgLinePath moveToPoint:firstModel.avgPoint];

    for (int i=1; i<pointArr.count; i++)
    {
        YKTimeLinePointModel *model = pointArr[i];
        [avgLinePath addLineToPoint:model.avgPoint];
    }

    avgLineLayer.path = avgLinePath.CGPath;
    avgLineLayer.lineWidth = 2.f;
    avgLineLayer.strokeColor = [UIColor colorWithRed:255.f/255.f green:215.f/255.f blue:0.f/255.f alpha:1.f].CGColor;
    avgLineLayer.fillColor = [UIColor clearColor].CGColor;

So far, we have finished drawing the time-sharing map, let's take a look at our results!

time-sharing chart

(6) Drawing breathing lights

how about it? Does it feel great? But I almost forgot one element just now, that is, the breathing light effect, which can best reflect the dynamic effect of our time-sharing map.

Then let's get to the code! (If you don't know much about CABasicAnimation, click here for a detailed introduction)

/**
 绘制呼吸灯
 */
- (void)drawBreathingLightWithPoint:(CGPoint)point
{
    CALayer *layer = [CALayer layer];
    //设置任意位置
    layer.frame = CGRectMake(point.x, point.y, 3, 3);
    //设置呼吸灯的颜色
    layer.backgroundColor = [UIColor blueColor].CGColor;
    //设置好半径
    layer.cornerRadius = 1.5;
    //给当前图层添加动画组
    [layer addAnimation:[self createBreathingLightAnimationWithTime:2] forKey:nil];

    [self.layer addSublayer:layer];
}


/**
 生成动画

 @param time 动画单词持续时间
 @return 返回动画组
 */
- (CAAnimationGroup *)createBreathingLightAnimationWithTime:(double)time
{
    //实例化CABasicAnimation
    CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    //从1开始
    scaleAnimation.fromValue = @1;
    //到3.5
    scaleAnimation.toValue = @3.5;
    //结束后不执行逆动画
    scaleAnimation.autoreverses = NO;
    //无限循环
    scaleAnimation.repeatCount = HUGE_VALF;
    //一次执行time秒
    scaleAnimation.duration = time;
    //结束后从渲染树删除,变回初始状态
    scaleAnimation.removedOnCompletion = YES;
    scaleAnimation.fillMode = kCAFillModeForwards;

    CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    opacityAnimation.fromValue = @1.0;
    opacityAnimation.toValue = @0;
    opacityAnimation.autoreverses = NO;
    opacityAnimation.repeatCount = HUGE_VALF;
    opacityAnimation.duration = time;
    opacityAnimation.removedOnCompletion = YES;
    opacityAnimation.fillMode = kCAFillModeForwards;

    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.duration = time;
    group.autoreverses = NO;
    group.animations = @[scaleAnimation, opacityAnimation];
    group.repeatCount = HUGE_VALF;
    //这里也应该设置removedOnCompletion和fillMode属性,以具体情况而定

    return group;
}

The final effect is as follows (a gif image was specially uploaded):

time-sharing chart

Well, our time-sharing chart is finally done. But don't be too happy in a hurry, in fact, there are still many shortcomings, such as:

  • Do themes and colors need to be configurable?
  • Do parameters and sizes need to be dynamically adapted?
  • Does the border line segment have to be customizable? Like a dashed line of another color?
  • Is this a stock class? How to modify the X-axis of the stock class?
  • How to modify the source code of the demo to become part of the strong extension framework?
  • …….

There are still many places and many effects worthy of our careful polishing! If there is a place to discuss, please feel free to shoot bricks and water!

Finally, I present a copy of the demo source code, and I will not thank you for taking it! Click here .

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324516566&siteId=291194637