利用核心图形库创建自定义控件-动态血压图

这是我第一次在上写文章,主要是之前有点懒。最近经过某人的提醒,觉得是时候写点东西总结一下了。先写一下写博客的原因和动力。

  • 使自己理解的更加深刻;
  • 给自己立一些时间戳,可以看到时间的轨迹;
  • 给小伙伴们一些参考,看了很多大神的文章,使小弟进步不少;
先说自定义控件的原因:
UIKit并没有提供所有的控件样式,比如像一些柱状图、饼图、曲线图等。类似的应用例如股票类APP里会经常出现自定义的控件。
再说一下这次要做的样式:
用图表去显示一个人的血压情况,并且点击控件具体部分显示详情。
先上图

利用核心图形库创建自定义控件-动态血压图
文章图片
16CD126D-5F44-416E-9467-02116FFD0ABF.png
从图片可以看出控件细节还是很多的,所以需要分解一下,这样更加容易理解和实现。
所以步骤应该是:
  1. 画底部直线和中间虚线;
  2. 画圆圈的连接线;
  3. 画圆圈;
  4. 根据点击事件位置画详情消息界面;
  5. 根据逻辑在需要绘制的时候调用;
  6. 添加事件
下面是提供的画这些小图形的具体方法
1、画底部直线和中间虚线
/** 画直线的方法 @param startPoint 开始点 @param endPoint 结束点 */ - (void)drawVerticalLineAtstartPoint:(CGPoint)startPoint endPonit:(CGPoint)endPoint { // 先获取当前画布(上下文) CGContextRef gc = UIGraphicsGetCurrentContext(); // 创建一个路径 CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, startPoint.x, startPoint.y); CGPathAddLineToPoint(path, NULL, endPoint.x, endPoint.y); // 将路径加到画布中去 CGContextAddPath(gc, path); // 画布的参数设置 CGContextSetStrokeColorWithColor(gc, self.lineBGColor.CGColor); CGContextSetLineWidth(gc, self.lineW); // 画布中画图 CGContextDrawPath(gc, kCGPathFillStroke); // 最后不要忘了去释放路径 CGPathRelease(path); }

/** 画虚线方法 @param startPoint 起始点 @param endPoint 结束点 @param color 虚线的颜色 */ - (void)drawDashVerticalLineAtstartPoint:(CGPoint)startPoint endPonit:(CGPoint)endPoint andColor:(UIColor *)color {CGContextRef gc = UIGraphicsGetCurrentContext(); CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, startPoint.x, startPoint.y); CGPathAddLineToPoint(path, NULL, endPoint.x, endPoint.y); CGContextAddPath(gc, path); // 虚线的间隔 CGFloat lengths[] = {2,2}; // 虚线的起始点 CGContextSetLineDash(gc, 0, lengths,2); CGContextSetStrokeColorWithColor(gc, color.CGColor); CGContextSetLineWidth(gc, self.lineW); CGContextDrawPath(gc, kCGPathFillStroke); CGPathRelease(path); // 将画布环境设置为实线 CGContextSetLineDash(gc, 0, NULL, 0); }

2、圆点的画连接线
/** 画连接线 @param leftPoint 起始点 @param rightPoint 结束点 */ - (void)drawBezierLinkLineWithLeftPoint:(CGPoint)leftPoint rightPoint:(CGPoint)rightPoint { // 此方法返回一个点,用来做控制点, CGPoint midPoint = midpoint(leftPoint, rightPoint); //用贝瑟尔曲线是为了微调连接线不至于僵硬 UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:leftPoint]; [path addQuadCurveToPoint:rightPoint controlPoint:midPoint]; [path setLineWidth:self.linkLineW]; CGContextRef gc = UIGraphicsGetCurrentContext(); CGContextSetStrokeColorWithColor(gc, self.linkLineColor.CGColor); [path stroke]; }

3、画圆圈
/** 画圆圈 @param color 颜色 @param isFill 实心还是空心 @param rect 画的位置 */ - (void)drawCircleImageWithColor:(UIColor *)color isFill:(BOOL)isFill AtRect:(CGRect)rect { CGContextRef gc = UIGraphicsGetCurrentContext(); CGContextAddArc(gc, CGRectGetMidX(rect), CGRectGetMidY(rect), 5, 0, M_PI*2, 0); if (isFill) { CGContextSetFillColorWithColor(gc, color.CGColor); } else { CGContextSetFillColorWithColor(gc, [UIColor whiteColor].CGColor); } CGContextSetStrokeColorWithColor(gc, color.CGColor); CGContextDrawPath(gc, kCGPathFillStroke); }

4、画消息框
/** 画消息框 @param point 消息框的尖尖指向的那个点 @param color 消息框的背景颜色 @param value 要显示的值 */ - (void)drawTopMsgAtPoint:(CGPoint)point andColor:(UIColor *)color andValue:(CGFloat)value { CGContextRef gc = UIGraphicsGetCurrentContext(); CGMutablePathRef path = CGPathCreateMutable(); CGFloat pointSpace = 6.0; CGFloat triangleH = 3.0; CGFloat rectWidth = 30.0; CGFloat rectHeight = 14.0; CGFloat triangleY = point.y-pointSpace; // 这里的点是三角形的尖尖 CGFloat triangleX = point.x; CGFloat rato = 3.0; // 这里的点是三角形的尖尖 CGPathMoveToPoint(path, NULL, triangleX, triangleY) ; // 画弧线 CGPathAddArcToPoint(path, NULL, triangleX-triangleH, triangleY-triangleH, triangleX-rectWidth/2, triangleY-triangleH, rato); CGPathAddArcToPoint(path, NULL, triangleX-rectWidth/2, triangleY-triangleH, triangleX-rectWidth/2, triangleY-triangleH-rectHeight, rato); CGPathAddArcToPoint(path, NULL, triangleX-rectWidth/2, triangleY-triangleH-rectHeight, triangleX+rectWidth/2, triangleY-triangleH-rectHeight, rato); CGPathAddArcToPoint(path, NULL, triangleX+rectWidth/2, triangleY-triangleH-rectHeight, triangleX+rectWidth/2, triangleY-triangleH, rato); CGPathAddArcToPoint(path, NULL, triangleX+rectWidth/2, triangleY-triangleH, triangleX+triangleH, triangleY-triangleH, rato); CGPathAddArcToPoint(path, NULL, triangleX+triangleH, triangleY-triangleH, triangleX, triangleY, rato); CGContextAddPath(gc, path); CGContextSetFillColorWithColor(gc, color.CGColor); CGContextDrawPath(gc, kCGPathFillStroke); CGPathRelease(path); NSString * valueStr = [NSString stringWithFormat:@"%.0f",value]; NSDictionary *valueAttributes = @{NSForegroundColorAttributeName:[UIColor whiteColor],NSFontAttributeName:[UIFont systemFontOfSize:12]}; // 计算字符串的大小 CGRect valueRect = [valueStr boundingRectWithSize:CGSizeMake(rectWidth, rectHeight) options:NSStringDrawingUsesLineFragmentOrigin attributes:valueAttributes context:nil]; CGContextSaveGState(gc); // 画字符串 [valueStr drawInRect:CGRectMake(triangleX-valueRect.size.width/2, triangleY-triangleH-rectHeight/2-valueRect.size.height/2, valueRect.size.width, valueRect.size.height) withAttributes:valueAttributes]; CGContextRestoreGState(gc); }

5、将这些方法在view绘制的时候通过一定的逻辑调用
这段代码内引用有其他逻辑,拷贝也不能运行,只是提供思路。
- (void)drawRect:(CGRect)rect { // Drawing code self.backgroundColor = [UIColor whiteColor]; CGFloat lastStarY = 0; CGFloat lastEndY = 0; CGFloat lastX = 0; CGFloat maxSpaceValue = https://www.it610.com/article/self.xueyaMaxandMinValue.topValue-self.xueyaMaxandMinValue.bottomValue; CGFloat maxSpacePt = self.topH-self.verticalSapce*2; // 这个比率是血压值和点的转换 CGFloat rato = maxSpacePt/maxSpaceValue; // 根据数据去画界面 for (int i = 0; i0) { XueYaValue *lastValue = self.xueYaValueArray[i-1]; if(![value.dateStr isEqualToString:lastValue.dateStr]) { [self drawDateStrAtX:x andValue:value.dateStr]; } } else { [self drawDateStrAtX:x andValue:value.dateStr]; }if ([self.currentValue isEqual:self.xueYaValueArray[i]]) {if (value.topValue<140) { [self drawBottomMsgAtPoint:CGPointMake(x, endY) andColor:[UIColor colorWithHexString:@"78B3F5"] andValue:self.currentValue.bottomValue]; [self drawTopMsgAtPoint:CGPointMake(x, starY) andColor:[UIColor colorWithHexString:@"78B3F5"] andValue:self.currentValue.topValue]; } else if (value.topValue<180) { [self drawBottomMsgAtPoint:CGPointMake(x, endY) andColor:[UIColor colorWithHexString:@"D97F5C"] andValue:self.currentValue.bottomValue]; [self drawTopMsgAtPoint:CGPointMake(x, starY) andColor:[UIColor colorWithHexString:@"D97F5C"] andValue:self.currentValue.topValue]; } else { [self drawBottomMsgAtPoint:CGPointMake(x, endY) andColor:[UIColor colorWithHexString:@"FF4B4B"] andValue:self.currentValue.bottomValue]; [self drawTopMsgAtPoint:CGPointMake(x, starY) andColor:[UIColor colorWithHexString:@"FF4B4B"] andValue:self.currentValue.topValue]; } } } }

6、最后添加事件
此处添加事件,画龙点睛。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = touches.anyObject; // 获取点击位置 CGPoint point = [touch locationInView:self]; // 计算需要交互的点 NSInteger index = (point.x - self.leftSpaceW+self.spaceW/2)/self.spaceW; NSLog(@"点击位置为:%@",NSStringFromCGPoint(point)); NSLog(@"点击index:%ld",index); if (self.xueYaValueArray.count>index) { // 此处存储需要绘制消息的内容 self.currentValue = https://www.it610.com/article/self.xueYaValueArray[index]; // 刷新界面,重新绘制, [self setNeedsDisplay]; } }

总结
CoreGraphics 主要用于自定义控件,实线个性化需求。
关于CoreGraphics 官方这么说明
这是一个绘图专用的API族,它经常被称为QuartZ或QuartZ 2D。Core Graphics是iOS上所有绘图功能的基石,包括UIKit。
【利用核心图形库创建自定义控件-动态血压图】所以是一组很强大的API,需要我们后续学习。

    推荐阅读