IOS 绘制心电图
这两天公司做一个医疗的项目,其中一个需求是接受传感器的病人心跳数据,将之在UI上面绘制成心电图. 在网上看了很多demo,废话不多说直接切入.(漏洞百出,欢迎批评指正)
因为除了逻辑其实很简单,代码就没有放到githup上面。点击打开链接 这是demo的下载地址
一.Core Graphics
Core Graphics 是一个基于c的api编写的图形核心绘制引擎。提供比较底层的,轻量级的二维渲染和非常好的输出保真度
使用Core Graphics画出你想要的东西,一个很重要的实情就是 你需要一个能够让你画图的地方(俗称 画布),基本上三中方法
//搬运别人的 总结就是这样
第一种:创建图片类型的画布。调用UIGraphicsBeginImageContextWithOptions函数就可获得用来处理图片的图形上下文。
第二种:利用cocoa自动为你生成画布。当你子类化了一个UIView并实现了自己的drawRect:方法后,一旦drawRect:方法被调用,Cocoa就会为你创建一个图形上下文,
这上面2种都是自己弄的画布,没有引用当前图像所在的画布,如果自己不想创建画布,可以用当前的画布(类型是:CGContextRef)
具体看代码:
画图展示需要我们自定义一个UIView,重写初始化方法.
- self.clearsContextBeforeDrawing = YES;
- - (instancetype)initWithFrame:(CGRect)frame{
-
- self = [super initWithFrame:frame];
- if (self) {
- self.clearsContextBeforeDrawing = YES;
- }
- return self;
- }
在重写系统方法- (void)drawRect:(CGRect)rect 这时候系统就知道 你要发挥你的绘画天赋了,并已经准好了
- - (void)drawRect:(CGRect)rect {
- [self drawCurve];
- [self drawGrid];
- }
//因为画的是心电图 所以 必须要有心跳的背景网格对不对 所以第一步 就是先画出背景的网格
-
- - (void)drawGrid{
-
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGFloat height = self.frame.size.height;
- CGFloat width = self.frame.size.width;
-
- CGFloat cell_square_width = 30;
-
- CGContextSetLineWidth(context, 1);
- CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
-
-
- NSInteger pos_x = 1;
- while (pos_x <= width) {
- CGContextSetLineWidth(context, 0.2);
-
- CGContextMoveToPoint(context, pos_x, 1);
- CGContextAddLineToPoint(context, pos_x, height);
- pos_x += cell_square_width;
- CGContextStrokePath(context);
- }
-
- NSInteger pos_y = 1;
-
- while (pos_y <= height) {
- CGContextMoveToPoint(context, 1, pos_y);
- CGContextAddLineToPoint(context, width,pos_y);
- pos_y += cell_square_width;
- CGContextStrokePath(context);
- }
- cell_square_width = cell_square_width / 5;
-
- pos_x = 1;
- while (pos_x <= width) {
- CGContextSetLineWidth(context, 0.2);
-
- CGContextMoveToPoint(context, pos_x, 1);
- CGContextAddLineToPoint(context, pos_x, height);
- pos_x += cell_square_width;
- CGContextStrokePath(context);
- }
-
- pos_y = 1;
-
- while (pos_y <= height) {
- CGContextMoveToPoint(context, 1, pos_y);
- CGContextAddLineToPoint(context, width,pos_y);
- pos_y += cell_square_width;
- CGContextStrokePath(context);
- }
-
- }
绘制玩背景 就要在背景上 开始描绘心跳了
-
- - (void)drawCurve{
-
- if (self.cunrrentPointCount == 0) {
- return;
- }
- CGFloat curveWidth = 0.8;
- CGContextRef currentContext = UIGraphicsGetCurrentContext();
- CGContextSetLineWidth(currentContext, curveWidth);
-
- CGContextSetStrokeColorWithColor(currentContext, [UIColor greenColor].CGColor);
-
- CGContextMoveToPoint(currentContext, self.points[0].x, self.points[0].y);
- for (int i = 0; i != self.cunrrentPointCount; ++ i) {
- if (self.points[i - 1].x < self.points[i].x) {
-
-
- CGContextAddLineToPoint(currentContext, self.points[i].x, self.points[i].y);
- } else {
- CGContextMoveToPoint(currentContext, self.points[i].x, self.points[i].y);
- }
- }
-
- CGContextStrokePath(currentContext);
-
-
- }
实现心电图有两种方式 一种是是平移的 就是我们经常在医院或者电视上看到的那种走纸带的,还有一种就是一个亮点在屏幕上来回移动画出心电图
-
- - (void)addPointAsRefreshChangeform:(CGPoint)point{
-
- static NSInteger currentPointsCount = 0;
- if (currentPointsCount < kMaxContainerCapacity) {
- self.numberOfRefreshElements = currentPointsCount + 1;
- self.refreshPointContainer[currentPointsCount] = point;
- currentPointsCount ++;
- }else{
-
- NSInteger workIndex = 0;
- while (workIndex != kMaxContainerCapacity - 1) {
- self.refreshPointContainer[workIndex] = self.refreshPointContainer[workIndex + 1];
- workIndex ++;
- }
- self.refreshPointContainer[kMaxContainerCapacity - 1] = point;
- self.numberOfRefreshElements = kMaxContainerCapacity;
- }
-
-
- }
经过一番折腾 为了便于理解画图 讲drawCruve得方法 简化
- - (void)drawCurve:(CGContextRef)ctx{
-
-
- CGContextSetLineWidth(ctx, lineWidth_LiveMonitor);
- CGContextSetStrokeColorWithColor(ctx, [UIColor greenColor].CGColor);
-
-
-
-
- CGContextAddLines(ctx, drawingPoints, _pointArray.count );
-
- CGContextStrokePath(ctx);
-
-
- }
有了drawrect里面的两个方法 就能完成画图 现在怎么样传递数据
写一个方法 用于外部调用. 用来传递数据
- - (void)fireDrawing{
-
-
- for (int i = 0 ; i < _pointArray.count ; i++) {
-
-
- if (testX < self.width) {
-
-
- drawingPoints[i] = CGPointMake(testX,self.height/2 - [_pointArray[i] intValue] / 10);
- testX = testX + pointMartin;
- } else {
- testX = 0;
- }
- }
-
-
- <span style="white-space:pre"> </span>
- CGRect reck = CGRectMake(testX - _pointArray.count * pointMartin, 0, + 10, self.height);
-
- dispatch_async(dispatch_get_main_queue(), ^{
- [self setNeedsDisplayInRect:reck];
- <pre name="code" class="objc">
testX = testX - pointMartin; }); }
外部 调用fireDrawing的时候 传进来数据 就可以实现绘制
在控制器中引入 头文件 开始使用数据绘图
- - (void)viewWillAppear:(BOOL)animated{
-
- [self popDataTimerEvent];
-
-
- }
- - (void)popDataTimerEvent{
- <span style="white-space:pre"> </span>
- self.popDataTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(popData) userInfo:nil repeats:YES];
- _index = 0;
-
- [[NSRunLoop currentRunLoop] addTimer:self.popDataTimer forMode:NSDefaultRunLoopMode];
-
-
- }
- - (void)popData{
-
-
-
-
- TripleECGView *view = self.ecgViewsArr[0];
- NSMutableArray *temparr = [NSMutableArray array];
- for (int i = 0; i < _drawingCountOnce; i++) {
- [temparr addObject:_testArr[_index + i]];
-
- }
-
-
-
- view.pointArray = temparr;
-
-
-
-
-
- <span style="white-space:pre"> </span>
- [view fireDrawing];
- _index += _drawingCountOnce;
- if (_index > 420) {
- _index = 0;
- }
-
-
-
-
-
- }
这时候 模拟器就会出现传递数据汇出的图形
![](https://img-blog.csdn.net/20160422173558610?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](https://img-blog.csdn.net/20160603154627357?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](https://img-blog.csdn.net/20160603154638412?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)