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

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

  • 使自己理解的更加深刻;
  • 给自己立一些时间戳,可以看到时间的轨迹;
  • 给小伙伴们一些参考,看了很多大神的文章,使小弟进步不少;

先说自定义控件的原因:
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 = self.xueyaMaxandMinValue.topValue-self.xueyaMaxandMinValue.bottomValue;
    CGFloat maxSpacePt = self.topH-self.verticalSapce*2;
    // 这个比率是血压值和点的转换
    CGFloat rato = maxSpacePt/maxSpaceValue;
    // 根据数据去画界面
    for (int i = 0; i<self.xueYaValueArray.count; i++) {
        
        CGFloat x = self.spaceW+i*self.leftSpaceW;
        
        XueYaValue  *value = self.xueYaValueArray[i];
        
        CGFloat starY =  fabs(maxSpacePt - (value.topValue-self.xueyaMaxandMinValue.bottomValue)*rato)+self.verticalSapce;
        ;
        CGFloat endY = fabs(maxSpacePt - (value.bottomValue-self.xueyaMaxandMinValue.bottomValue)*rato)+self.verticalSapce;
        
        
        
        if (i<self.xueYaValueArray.count-1) {
            lastX = x+self.spaceW;
            XueYaValue  *lastValue = self.xueYaValueArray[i+1];
            lastStarY = fabs(maxSpacePt - (lastValue.topValue-self.xueyaMaxandMinValue.bottomValue)*rato)+self.verticalSapce;
            lastEndY = fabs(maxSpacePt - (lastValue.bottomValue-self.xueyaMaxandMinValue.bottomValue)*rato)+self.verticalSapce;
            
            
        }
        
        [self drawVerticalLineAtstartPoint:CGPointMake(x, 0) endPonit:CGPointMake(x, starY)];
        [self drawVerticalLineAtstartPoint:CGPointMake(x, endY) endPonit:CGPointMake(x, self.topH)];
        // 这里140,180,是临界点,用来区分颜色的
        if (value.topValue<140) {
            [self drawDashVerticalLineAtstartPoint:CGPointMake(x, starY) endPonit:CGPointMake(x, endY) andColor:[UIColor colorWithHexString:@"78B3F5"]];
        }
        else if(value.topValue<180)
        {
            [self drawDashVerticalLineAtstartPoint:CGPointMake(x, starY) endPonit:CGPointMake(x, endY) andColor:[UIColor colorWithHexString:@"D97F5C"]];
        }
        else
        {
            [self drawDashVerticalLineAtstartPoint:CGPointMake(x, starY) endPonit:CGPointMake(x, endY) andColor:[UIColor colorWithHexString:@"FF4B4B"]];
        }
        if (i<self.xueYaValueArray.count-1) {
            [self drawBezierLinkLineWithLeftPoint:CGPointMake(lastX, lastStarY) rightPoint:CGPointMake(x, starY)];
            [self drawBezierLinkLineWithLeftPoint:CGPointMake(lastX, lastEndY) rightPoint:CGPointMake(x, endY)];
        }
        
        if (value.topValue<140) {
            [self drawCircleImageWithColor:[UIColor colorWithHexString:@"78B3F5"] isFill:YES AtRect:CGRectMake(x-5, starY-5, 10, 10)];
            [self drawCircleImageWithColor:[UIColor colorWithHexString:@"78B3F5"] isFill:YES AtRect:CGRectMake(x-5, endY-5, 10, 10)];
        }
        else if (value.topValue<180)
        {
            [self drawCircleImageWithColor:[UIColor colorWithHexString:@"D97F5C"] isFill:YES AtRect:CGRectMake(x-5, starY-5, 10, 10)];
            [self drawCircleImageWithColor:[UIColor colorWithHexString:@"D97F5C"] isFill:YES AtRect:CGRectMake(x-5, endY-5, 10, 10)];
        }
        else
        {
            [self drawCircleImageWithColor:[UIColor colorWithHexString:@"FF4B4B"] isFill:YES AtRect:CGRectMake(x-5, starY-5, 10, 10)];
            [self drawCircleImageWithColor:[UIColor colorWithHexString:@"FF4B4B"] isFill:YES AtRect:CGRectMake(x-5, endY-5, 10, 10)];
        }
        
        [self drawTimeStrAtX:x andValue:value.timeStr];
        if (i>0) {
            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<UITouch *> *)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 = self.xueYaValueArray[index];
        // 刷新界面,重新绘制,
        [self setNeedsDisplay];
    }
}

总结

CoreGraphics 主要用于自定义控件,实线个性化需求。
关于CoreGraphics 官方这么说明

这是一个绘图专用的API族,它经常被称为QuartZ或QuartZ 2D。Core Graphics是iOS上所有绘图功能的基石,包括UIKit。

所以是一组很强大的API,需要我们后续学习。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 目录: 主要绘图框架介绍 CALayer 绘图 贝塞尔曲线-UIBezierPath CALayer子类 补充:i...
    Ryan___阅读 1,658评论 1 9
  • iOS 苹果官方Demo合集 字数10517阅读21059评论18喜欢144 其实, 开发了这么久, 不得不说, ...
    bingo居然被占了阅读 10,092评论 2 31
  • EPISODE 11 人生多是无奈·没有硝烟的战场 东煌集团总部大厦22楼,黑色的大理石铺满整个地面与墙面,而顶上...
    紫上薰阅读 571评论 0 49
  • 2月14日,你知道今天什么节日吗?没错,就是情人节!下面开始播报天气预报,今天你的朋友圈将会阴晴并布,情侣档秀恩爱...
    夜科技阅读 447评论 0 0
  • 我失恋了,在几天前。同时面临的是我的各项毕业考试,招聘考试。很庆幸自己大学几年里人缘还不错,我的好朋友们会放下复习...
    阳光太暖阅读 347评论 0 2