昨晚睡觉时qq聊天中看见别人的截图,截图内容为健康类app运动数据的展示截图,在坐标系中展示曲线数据,感觉很炫,今天自己动手写了一下,JDataCurveView封装了一下。
JDataCurveView头文件如下:
@interface JDataCurveView : UIView
//如果显示数据需经常刷新数据,值该值为yes,否则no
@property(nonatomic,assign)BOOL isDynamicRefreshData;
//Y坐标轴名称
@property(nonatomic,copy)NSString* yCoordinateName;
//Y坐标轴的单位名称
@property(nonatomic,copy)NSString* yCoordinateUnitName;
//X坐标轴名称
@property(nonatomic,copy)NSString* xCoordinateName;
//Y坐标轴的单位名称
@property(nonatomic,copy)NSString* xCoordinateUnitName;
//曲线条颜色
@property(nonatomic,strong)UIColor *curveColor;
//单位名称显示颜色
@property(nonatomic,strong)UIColor* unitNameColor;
//单位数据显示的颜色
@property(nonatomic,strong)UIColor* unitDataDisplayColor;
//坐标轴颜色
@property(nonatomic,strong)UIColor* coordinateColor;
//y轴显示数据
@property(nonatomic,strong)NSArray *yDisplayData;
//x轴显示数据
@property(nonatomic,strong)NSArray *xDisplayData;
//中间曲线数据
@property(nonatomic,strong)NSArray *dataArrary;
//是否填充曲线下部分内容
@property(nonatomic,assign)BOOL isNeedFill;
//填充曲线下部分内容颜色值
@property(nonatomic,strong)UIColor *fillColor;
@property(nonatomic,assign)CGFloat xMaxData;
@property(nonatomic,assign)CGFloat yMaxData;
@property(nonatomic,assign)BOOL isNeedDisplayXData;
@property(nonatomic,assign)BOOL isNeedDisplayYData;
+(JDataCurveView*)dataCurveView;
-(void)resetDataArray:(NSArray *)dataArrary;
@end
JDataCurveView源文件,具体如下:
#import "JDataCurveView.h"
@interface JDataCurveView ()
@property(nonatomic,strong)UIImage *coordinateBackgroundImg;
@property(nonatomic,strong)UIImage *dataDispalyImg;
@property(nonatomic,strong)UIImageView *coordinateBackgroundImgView;
@property(nonatomic,strong)UIImageView *dataDispalyImgView;
@property(nonatomic,strong)dispatch_queue_t queue;
@end
@implementation JDataCurveView
+(JDataCurveView*)dataCurveView
{
return [[JDataCurveView alloc] init];
}
-(id)init
{
if (self = [super init]) {
[self setupUI];
}
return self;
}
-(void)setupUI
{
_isNeedDisplayXData = YES;
_isNeedDisplayYData = YES;
_coordinateBackgroundImgView = [[UIImageView alloc] init];
[self addSubview:_coordinateBackgroundImgView];
_dataDispalyImgView = [[UIImageView alloc] init];
[self addSubview:_dataDispalyImgView];
}
-(void)layoutSubviews
{
_coordinateBackgroundImgView.frame = self.bounds;
_dataDispalyImgView.frame = CGRectMake(40 + 1, 35 + 10, self.bounds.size.width - 30 - 41 - 10, self.bounds.size.height - 50 - 35 - 1 - 10);
}
drawRect绘画代码如下,采取后台线程绘制方法,避免阻塞主线程:
-(void)drawRect:(CGRect)rect
{
if (!_coordinateBackgroundImg) {
[self drawCoordinateBackgroundImg];
}
if (_isDynamicRefreshData) {
[self drawDataImg];
}
else
{
if (!_dataDispalyImg) {
[self drawDataImg];
}
}
}
drawCoordinateBackgroundImg的具体实现:
-(void)drawCoordinateBackgroundImg
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIGraphicsBeginImageContext(self.bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
if (!_unitNameColor) {
_unitNameColor = [UIColor grayColor];
}
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
if (!_yCoordinateName) {
_yCoordinateName = @"Y轴";
}
NSString *yCoordinateName = [NSString stringWithFormat:@"%@:%@",_yCoordinateName,_yCoordinateUnitName?_yCoordinateUnitName:@""];
[yCoordinateName drawAtPoint:CGPointMake(10,10) withAttributes:@{NSForegroundColorAttributeName:_unitNameColor}];
if (!_xCoordinateName) {
_xCoordinateName = @"X轴";
}
NSString *xCoordinateName = [NSString stringWithFormat:@"%@:%@",_xCoordinateName,_xCoordinateUnitName?_xCoordinateUnitName:@""];
[xCoordinateName drawAtPoint:CGPointMake(self.bounds.size.width - 30 - 25, self.bounds.size.height - 20) withAttributes:@{NSForegroundColorAttributeName:_unitNameColor}];
if (!_coordinateColor) {
_coordinateColor = [UIColor grayColor];
}
CGContextSetStrokeColorWithColor(context, _coordinateColor.CGColor);
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
//画坐标系
[bezierPath moveToPoint:CGPointMake(40, 35)];
[bezierPath addLineToPoint:CGPointMake(40, self.bounds.size.height - 50)];
[bezierPath addLineToPoint:CGPointMake(self.bounds.size.width - 30, self.bounds.size.height - 50)];
if (!_unitDataDisplayColor) {
_unitDataDisplayColor = [UIColor whiteColor];
}
//画Y坐标刻度
if (_yDisplayData && _yDisplayData.count > 0) {
CGFloat yMaxData = [_yDisplayData[_yDisplayData.count - 1] floatValue];
CGFloat yMaxLength = self.bounds.size.height - 35 - 50 - 10;
CGFloat orgY = self.bounds.size.height - 50;
for (NSNumber *yNum in _yDisplayData) {
CGFloat length = yMaxLength * ([yNum floatValue] / yMaxData);
if ([yNum floatValue] != 0) {
CGContextMoveToPoint(context, 40, orgY - length);
CGContextAddLineToPoint(context, 45, orgY - length);
}
if (_isNeedDisplayYData) {
NSString *dataStr = [NSString stringWithFormat:@"%@",yNum];
[dataStr drawAtPoint:CGPointMake(10, orgY - length - 10) withAttributes:@{NSForegroundColorAttributeName:_unitDataDisplayColor}];
}
}
}
//画X坐标刻度
if (_xDisplayData && _xDisplayData.count > 0) {
CGFloat xMaxData = [_xDisplayData[_xDisplayData.count - 1] floatValue];
CGFloat xMaxLength = self.bounds.size.width - 40 - 30 - 10;
CGFloat orgX = 40;
for (NSNumber *xNum in _xDisplayData) {
CGFloat length = xMaxLength * ([xNum floatValue] / xMaxData);
if ([xNum floatValue] != 0) {
CGContextMoveToPoint(context,orgX + length, self.bounds.size.height - 50);
CGContextAddLineToPoint(context, orgX + length, self.bounds.size.height - 50 - 5);
}
if (_isNeedDisplayXData) {
NSString *dataStr = [NSString stringWithFormat:@"%@",xNum];
[dataStr drawAtPoint:CGPointMake(orgX + length - 8, self.bounds.size.height - 50 + 5) withAttributes:@{NSForegroundColorAttributeName:_unitDataDisplayColor}];
}
}
}
CGContextAddPath(context, bezierPath.CGPath);
CGContextStrokePath(context);
_coordinateBackgroundImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
_coordinateBackgroundImgView.image = _coordinateBackgroundImg;
});
});
}
drawDataImg实现:
-(void)drawDataImg
{
if (!_dataArrary || _dataArrary.count <=0) {
return;
}
dispatch_queue_t queue = NULL;
if (_isDynamicRefreshData) {
//创建串行队列,保证一张图片一张图片的显示,不乱
//创建串行队列
if(!_queue)
{
_queue = dispatch_queue_create("com.draw.dataImg", DISPATCH_QUEUE_SERIAL);
}
queue = _queue;
}
else
{
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
}
dispatch_async(queue, ^{
UIGraphicsBeginImageContext(_dataDispalyImgView.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
CGPoint point = [_dataArrary[0] CGPointValue];
CGFloat xLength = _dataDispalyImgView.frame.size.width;
CGFloat yLength = _dataDispalyImgView.frame.size.height;
CGFloat xValue = point.x;
CGFloat yValue = point.y;
[[UIColor clearColor] setFill];
if (!_curveColor) {
_curveColor = [UIColor whiteColor];
}
[_curveColor setStroke];
CGContextFillEllipseInRect(context, _dataDispalyImgView.bounds);
[bezierPath moveToPoint:CGPointMake((xValue / _xMaxData) * xLength, yLength - (yValue / _yMaxData ) * yLength)];
for (NSValue *value in _dataArrary)
{
point = [value CGPointValue];
xValue = point.x;
yValue = point.y;
[bezierPath addLineToPoint:CGPointMake((xValue / _xMaxData) * xLength, yLength - (yValue / _yMaxData ) * yLength)];
}
CGContextAddPath(context, bezierPath.CGPath);
CGContextDrawPath(context, kCGPathFillStroke);
CGContextFillPath(context);
_dataDispalyImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
_dataDispalyImgView.image = _dataDispalyImg;
});
});
}
重新设置数据源:
-(void)resetDataArray:(NSArray *)dataArrary
{
if (_isDynamicRefreshData) {
_dataArrary = dataArrary;
[self drawDataImg];
}
}
回头看看实现的思路,不过先看看截图就好说一点:
这个绘画总共有两张图片构成,整的背景图片为整个坐标系,因为整个坐标系不会变化,只要绘制一次就可以,宁外红色部分是数据展示部分,因为数据部分可能改变,如显示人的脉搏跳动数据 实时改变,所以实时绘制,因此创建了一个串行队列,在串行队列中绘制,保证数据会被一张一张的绘制,一张一张的显示,不会乱。
现在看看使用方式:
-(void)testDataCurveView1
{
//随机参数数据,模拟情况
NSMutableArray *dataArray = [NSMutableArray array];
for( float i = 0; i<= 70 ;i+=1)
{
CGFloat value = arc4random() % 201;
CGPoint point = CGPointMake(i, value);
[dataArray addObject:[NSValue valueWithCGPoint:point]];
}
JDataCurveView *dataCurveView = [JDataCurveView dataCurveView];
dataCurveView.frame = CGRectMake(10, 64, self.view.frame.size.width - 20, 250);
dataCurveView.yCoordinateName = @"海拔";
dataCurveView.yCoordinateUnitName = @"米";
dataCurveView.xCoordinateName = @"时间";
dataCurveView.xCoordinateUnitName = @"分";
dataCurveView.yDisplayData = @[@0,@50,@100,@150,@200];
dataCurveView.xDisplayData = @[@0,@20,@40,@60,@70];
dataCurveView.dataArrary = dataArray;
dataCurveView.curveColor = [UIColor blueColor];
dataCurveView.xMaxData = 70;
dataCurveView.yMaxData = 200;
[self.view addSubview:dataCurveView];
}
-(void)testDataCurveView2
{
_dArray = [NSMutableArray array];
//随机参数数据,模拟情况
for( float i = 0; i<= 80 ;i+=1)
{
CGFloat value = arc4random() % 45 + 20;
CGPoint point = CGPointMake(i, value);
[self.dArray addObject:[NSValue valueWithCGPoint:point]];
}
JDataCurveView *dataCurveView = [JDataCurveView dataCurveView];
dataCurveView.frame = CGRectMake(10, 340, self.view.frame.size.width - 20, 250);
dataCurveView.isDynamicRefreshData = YES;
dataCurveView.yCoordinateName = @"脉搏";
dataCurveView.yCoordinateUnitName = @"次";
dataCurveView.xCoordinateUnitName = @"无";
dataCurveView.yDisplayData = @[@0,@20,@40,@60,@80];
dataCurveView.xDisplayData = @[@0,@20,@40,@60,@80];
dataCurveView.isNeedDisplayXData = NO;
dataCurveView.dataArrary = self.dArray;
dataCurveView.curveColor = [UIColor brownColor];
dataCurveView.xMaxData = 80;
dataCurveView.yMaxData = 80;
_dataCurveView = dataCurveView;
[self.view addSubview:dataCurveView];
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
-(void)timerAction
{
NSMutableArray *array = [NSMutableArray array];
for(int i = 1;i<self.dArray.count;i++)
{
NSValue *value = self.dArray[i];
CGPoint point = [value CGPointValue];
[array addObject:[NSValue valueWithCGPoint:CGPointMake(point.x - 1, point.y)]];
}
[self.dArray removeAllObjects];
CGFloat value = arc4random() % 45 + 20;
CGPoint point = CGPointMake(80, value);
[array addObject:[NSValue valueWithCGPoint:point]];
self.dArray = array;
[_dataCurveView resetDataArray:self.dArray];
}
}
看看运行结果,因为会实时绘制,所以我做成了gif图片,便于展示,如下:
代码上传github:https://github.com/jiangtaidi/DataCurveDemo.git 感兴趣的可以下载运行一下,希望对你有用!