本篇介绍如何使用CoreGraphics绘制一个坐标系
为了学习如何绘制各种图表,特意去网上找了很多资料,看到了《Cocoa-Charts》这个开源框架,就学习了一下,在这里做一下分解后的记录。
原框架中画了很多类型的图表,包括折线图、柱状图、圆饼图、环形图以及区域填充图等等。我将在本篇开始逐个介绍这些图表是如何从零开始,到完全绘制成功。还有很多其它类型的图表都可以基于这几个基本类型去扩展。
CoreGraphics绘图三板斧
先大体说说CoreGraphics绘图的一些常用的方法,先有个大致了解。后面绘制各类图表基本上就是围绕这几个基本思想去做。
1.获取绘图上下文
CGContextRef context = UIGraphicsGetCurrentContext();
无论你想使用CoreGraphics做什么,你都得先获取到绘图上下文,我们就是依靠它来进行各种绘图操作。
2.画一条线
在绘制区域画一条线,你最少需要用到五个方法。
- 设置线宽
CGContextSetLineWidth(context, 1);
- 设置线的起始点
CGContextMoveToPoint(context, 10, 20);
- 设置线的终点,形成一条绘制路径
CGContextAddLineToPoint(context, 100, 100);
- 设置画笔的颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
- 使用画笔画线
CGContextStrokePath(context);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1);
CGContextMoveToPoint(context, 10, 20);
CGContextAddLineToPoint(context, 100, 100);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextStrokePath(context);
3.画一个矩形
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置边框线宽
CGContextSetLineWidth(context, 2);
// 添加一个矩形路径
CGContextAddRect(context, CGRectMake(0, 0, 50, 50));
// 设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
// 绘制路径
CGContextStrokePath(context);
4.画一个圆弧或者圆
CGContextRef context = UIGraphicsGetCurrentContext();
// 给定圆周率π
CGFloat pi = 3.141592653f;
// 添加一个圆
CGContextAddArc(context, 100, 100, 50, 0, pi/2, 0);
// 设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
// 绘制圆弧
CGContextStrokePath(context);
解释一下 CGContextAddArc
函数的七个参数:
第一个是绘图上下文
第二个是圆心的X坐标
第三个是圆心的Y坐标
第四个是圆的半径
第五个是圆弧的起始弧度
第六个是圆弧的结束弧度
第七个是你是否要指定按照顺时针方向,1为顺时针,0为逆时针
我们示例中起始弧度是0度,结束弧度是π/2,顺时针画就是效果图中的1/4圆。
但是细心地同学可能已经看出问题来了,我在 CGContextAddArc
函数中设置的第七个参数是 0,对!0代表逆时针。那为什么逆时针会画出1/4圆,而不是3/4圆呢?
我去官方文档找了一下介绍《CGContextAddArc》:
The clockwise parameter determines the direction in which the arc is created; the actual direction of the final path is dependent on the current transformation matrix of the graphics context. In a flipped coordinate system (the default for UIView drawing methods in iOS), specifying a clockwise arc results in a counterclockwise arc after the transformation is applied.
翻译:
顺时针参数确定创建弧的方向;最终路径的实际方向取决于图形上下文的当前转换矩阵。在翻转的坐标系中(iOS中UIView绘制方法的默认值),指定顺时针的圆弧会在应用转换后产生逆时针的圆弧。
看明白了吧,默认的就是翻转的,所以你自己没有翻转坐标系的情况下,就理解为 1 是逆时针,0 是顺时针就可以了。
4.1再来画一个,基于上面的例子画一个 1/4圆的扇形
与上面的不同点就是我们要给出起点位置 CGContextMoveToPoint
,也就是圆弧的圆心。另外我们要把起点和终点闭合 CGContextClosePath
,这样的封闭区域才能画出一个扇形
CGContextRef context = UIGraphicsGetCurrentContext();
// 给定圆周率π
CGFloat pi = 3.141592653f;
// 起始点移动到圆心
CGContextMoveToPoint(context, 100, 100);
// 添加一个圆
CGContextAddArc(context, 100, 100, 50, 0, pi/2, 0);
// 设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
// 闭合路径,让起点和终点连接起来
CGContextClosePath(context);
// 绘制圆弧
CGContextStrokePath(context);
效果图:
5.填充指定区域
就比如我们填充刚刚画的那个 1/4圆的扇形
CGContextRef context = UIGraphicsGetCurrentContext();
// 给定圆周率π
CGFloat pi = 3.141592653f;
// 起始点移动到圆心
CGContextMoveToPoint(context, 100, 100);
// 添加一个圆
CGContextAddArc(context, 100, 100, 50, 0, pi/2, 0);
// 设置填充颜色
CGContextSetFillColorWithColor(context, [UIColor orangeColor].CGColor);
// 填充
CGContextFillPath(context);
效果图:
注意:我这里并没有闭合路径,但是填充依然会成功。因为填充行为会自动为你将起点和终点连接起来(即闭合路径)。但是 StrokePath
画笔画线则不然,它不会自动闭合路径。
我现在给出画1/4圆扇形时错误的例子:
❌错误1:不给出起点也不调用闭合函数就如图 《一段圆弧》所示
❌错误2:画1/4圆扇形的时候,给出起点但不调用闭合函数
CGContextRef context = UIGraphicsGetCurrentContext();
// 给定圆周率π
CGFloat pi = 3.141592653f;
// 起始点移动到圆心
CGContextMoveToPoint(context, 100, 100);
// 添加一个圆
CGContextAddArc(context, 100, 100, 50, 0, pi/2, 0);
// 设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
// 不调用闭合函数
// 绘制圆弧
CGContextStrokePath(context);
效果图:
❌错误3:填充 1/4 扇形的时候,没有把圆心设为起点
CGContextRef context = UIGraphicsGetCurrentContext();
// 给定圆周率π
CGFloat pi = 3.141592653f;
// 起点未移动到圆心
// 添加一个圆
CGContextAddArc(context, 100, 100, 50, 0, pi/2, 0);
// 设置填充颜色
CGContextSetFillColorWithColor(context, [UIColor orangeColor].CGColor);
// 填充
CGContextFillPath(context);
效果图:
错误原因:之所以只填充了一个“月牙”,还是因为自动闭合路径的缘故。因为没有把圆心设为起点,所以它闭合的是圆弧的起点和终点。就是这段弧:
6.绘制文字
// 设置字体
UIFont *textFont= [UIFont systemFontOfSize:15];
// 设置属性
NSDictionary *attrs = @{NSFontAttributeName:textFont,
NSForegroundColorAttributeName:[UIColor blackColor]};
// 文字的绘制区域
CGRect textRect = CGRectMake(100, 100, 100, 100);
// 绘制文字
[@"Hello World!" drawInRect:textRect withAttributes:attrs];
效果图:
7. 其它要点
CGContextAddLineToPoint
添加完线段后,起始点也移动到新的端点处,再接CGContextAddLineToPoint
会从新的端点处开始画CGContextFillPath
和CGContextStrokePath
会消耗掉当前context中的点和线(即路径),再次调用需要重新设置起始点或路径CGContextFillPath
填充最多可以两条线的路径实现闭合,三条线的路径就失灵了。这条是亲测的!!!CGContextClosePath
在路径的终点和起点之间追加一条线。如果你打算填充一段路径,那么就不需要使用该命令,因为该命令会被自动调用。-
两个不相干的封闭路径都可以被一次性正常填充,比如两个矩形
前面这些准备工作做完,就可以去画各种图表了,下面开始进入正题
绘制坐标系
万事开头难,绝大多数图表的绘制都是从坐标系开始的,首先要学会建立一个简单实用的坐标系,为之后绘制各类图表打下一个好基础。
之前介绍过,CoreText的坐标系与我们平时Coding时使用的UIKit坐标系Y轴是倒转的。在这里介绍的CoreGraphics坐标系就不用担心这个问题,我们在绘制过程中用的CoreGraphics坐标系与UIKit完全一致,即原点在屏幕左上角,X轴向右为正方向,Y轴向下为正方向。
坐标系的结构组成:
外边框
X轴线
Y轴线
纬线(横向刻度线,黄色虚线)
经线(纵向刻度线,黄色虚线)
X轴刻度(X轴上的标题)
Y轴刻度(Y轴上的标题)
十字交叉线(显示触摸点的横纵信息)
总共就上面这几点,这也是绘制一个坐标系的基本思路。
首先,绘制坐标系的外边框
与CoreText一样的是,所有的绘制操作都是从drawRect:
开始,而且同样要获取绘图上下文 CGContextRef
。
创建一个继承自 UIView
的类,取名 CoordinateSystem
,用于绘制坐标系。在 CoordinateSystem
的 drawRect:
方法中Coding。
好,开始绘制外边框。获取绘图上下文 CGContextRef
,并设置绘制区域的填充色,进行填充。
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor);
CGContextFillRect(context, rect);
}
现在可以去 ViewController
加载一下 CoordinateSystem
,背景颜色设置为cyanColor
,效果如下
上图就是填充后的样子,整个绘制区域被填充成 cyanColor
。
接下来,设置边框线宽-->设置起始点-->在绘制区域添加一个矩形路径-->设置画笔颜色-->绘制路径
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor);
CGContextFillRect(context, rect);
CGContextSetLineWidth(context, 2);
CGContextMoveToPoint(context, 0.0f, 0.0f);
CGContextAddRect(context, rect);
CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
CGContextStrokePath(context);
}
效果如下:
绘制X轴线
绘制的X轴线要注意,不能紧贴绘制区域底部,因为后面还要绘制X轴上的刻度,要留出一部分空间。下部间隙暂定 15 像素。
static CGFloat axisMarginBottom = 15;
- (void)drawRect:(CGRect)rect {
// 获取绘图上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置背景填充色
CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor);
// 填充整个绘制区域
CGContextFillRect(context, rect);
// 设置边框线宽
CGContextSetLineWidth(context, 2);
// 设置起始点
CGContextMoveToPoint(context, 0.0f, 0.0f);
// 添加一个矩形路径
CGContextAddRect(context, rect);
// 设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
// 绘制路径
CGContextStrokePath(context);
// 重新设置X轴线宽
CGContextSetLineWidth(context, 1);
// 设置绘制X轴的起始点
CGContextMoveToPoint(context, 0.0f, rect.size.height - axisMarginBottom);
// 添加绘制X轴线的路径
CGContextAddLineToPoint(context, rect.size.width, rect.size.height - axisMarginBottom);
// 设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 绘制路径
CGContextStrokePath(context);
}
效果如下:
绘制Y轴线
绘制Y轴线要在左侧留出一部分空间,与X轴对称,暂定左侧间距 15 像素
static CGFloat axisMarginBottom = 15;
static CGFloat axisMarginLeft = 15;
- (void)drawRect:(CGRect)rect {
// 获取绘图上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置背景填充色
CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor);
// 填充整个绘制区域
CGContextFillRect(context, rect);
// 设置边框线宽
CGContextSetLineWidth(context, 2);
// 设置起始点
CGContextMoveToPoint(context, 0.0f, 0.0f);
// 添加一个矩形路径
CGContextAddRect(context, rect);
// 设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
// 绘制路径
CGContextStrokePath(context);
// 重新设置X轴线宽
CGContextSetLineWidth(context, 1);
// 设置绘制X轴的起始点
CGContextMoveToPoint(context, 0.0f, rect.size.height - axisMarginBottom);
// 添加绘制X轴线的路径
CGContextAddLineToPoint(context, rect.size.width, rect.size.height - axisMarginBottom);
// 设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 绘制路径
CGContextStrokePath(context);
// 绘制Y轴线
CGContextMoveToPoint(context, axisMarginLeft, 0.0f);
CGContextAddLineToPoint(context, axisMarginLeft, rect.size.height);
CGContextStrokePath(context);
}
效果图:
绘制经线(纵向刻度线、虚线)
纵向的刻度线可以根据数据源的个数,也可以自定固定的个数,我这里是以数据源的个数为准,有多少组数据画多少条经线。
坐标系右侧留出一定空间,暂定右侧间距为 axisMarginRight
3 像素。
- (void)drawLongitudeLines:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1);
CGContextSetStrokeColorWithColor(context, [UIColor orangeColor].CGColor);
if ([self.longitudeTitles count] <= 0) {
return;
}
//设置线条为虚线
CGFloat lengths[] = {3.0, 2.0};
CGContextSetLineDash(context, 0.0, lengths, 2);
CGFloat postOffset;
CGFloat offset;
postOffset = (rect.size.width - axisMarginLeft - axisMarginRight) / (self.longitudeTitles.count - 1);
offset = axisMarginLeft;
for (int i = 1; i < self.longitudeTitles.count ; i++) {
CGContextMoveToPoint(context, offset + i * postOffset, 0);
CGContextAddLineToPoint(context, offset + i * postOffset, rect.size.height - axisMarginBottom);
}
CGContextStrokePath(context);
CGContextSetLineDash(context, 0, nil, 0);
}
思路:
postOffset
是经线之间的间距,比如要绘制8条经线,那么就把整个绘制区域平均分成7份,每一份就是一个 postOffset
。
offset
是绘制起始点的X轴坐标,也就是坐标系在左侧留出的空间 axisMarginLeft
。
这里说明一下设置虚线的参数 lengths
,数组第一个参数 3.0
代表线段长度,第二个参数 2.0
代表线段间距。
CGFloat lengths[] = {3.0, 2.0};
CGContextSetLineDash(context, 0.0, lengths, 2);
效果图:
绘制X轴刻度
X轴刻度需要相应的数据源,所以 CoordinateSystem
类要能够接收X轴刻度(X轴标题)数据源,因此添加一个属性 NSArray *longitudeTitles
来接收。
单独写一个方法来绘制X轴刻度:
- (void)drawXAxisTitles:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5f);
if ([self.longitudeTitles count] <= 0) {
return;
}
CGFloat postOffset;
CGFloat offset;
postOffset = (rect.size.width - axisMarginLeft - axisMarginRight) / (self.longitudeTitles.count - 1);
offset = axisMarginLeft;
for (int i = 0; i < [self.longitudeTitles count]; i++) {
// 绘制线条
NSString *valueStr = (NSString *) [self.longitudeTitles objectAtIndex:i];
UIFont *textFont= [UIFont systemFontOfSize:12]; //设置字体
NSMutableParagraphStyle *textStyle=[[NSMutableParagraphStyle alloc]init];//段落样式
textStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attrs = @{NSFontAttributeName:textFont,
NSParagraphStyleAttributeName:textStyle,
NSForegroundColorAttributeName:[UIColor blackColor]};
CGSize textSize = [valueStr boundingRectWithSize:CGSizeMake(100, 100)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attrs
context:nil].size;
// 调整X轴坐标位置
// 第一个刻度的位置要绘制在Y轴线右侧
if (i == 0) {
CGRect textRect= CGRectMake(axisMarginLeft, rect.size.height - axisMarginBottom, textSize.width, textSize.height);
textStyle.alignment=NSTextAlignmentLeft;
// 绘制字体
[valueStr drawInRect:textRect withAttributes:attrs];
}
// 最后一个刻度的位置要绘制在最后一条经线的左侧
else if (i == self.longitudeTitles.count-1) {
CGRect textRect= CGRectMake(rect.size.width - axisMarginRight - textSize.width, rect.size.height - axisMarginBottom, textSize.width, textSize.height);
textStyle.alignment=NSTextAlignmentRight;
// 绘制字体
[valueStr drawInRect:textRect withAttributes:attrs];
} else {
CGRect textRect= CGRectMake(offset + (i-0.5) * postOffset, rect.size.height - axisMarginBottom, postOffset, textSize.height);
textStyle.alignment=NSTextAlignmentCenter;
// 绘制字体
[valueStr drawInRect:textRect withAttributes:attrs];
}
}
}
思路:
把你要绘制在X轴上的刻度数据传进 longitudeTitles
数组中,逐个设置属性,计算 Size,确定每一个刻度的位置,最后使用字符串调用 drawInRect: withAttributes:
方法绘制字体。
效果图:
绘制纬线(横向刻度线、虚线)
与绘制经线一样,我们以数据源的个数为准,Y轴绘制纬线的数据源定为 NSArray *latitudeTitles
。
我在绘制纬线的时候在上部也同样留出了一定的空间,不让它顶格,好看一点,间距是 axisMarginTop
3 像素
- (void)drawLatitudeLines:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1);
CGContextSetStrokeColorWithColor(context, [UIColor orangeColor].CGColor);
if ([self.latitudeTitles count] <= 0){
return ;
}
//设置线条为虚线
CGFloat lengths[] = {3.0, 3.0}; // 线宽和间距,长短相间可以画两条虚线,然后拼接在一起
CGContextSetLineDash(context, 0.0, lengths, 2);
CGFloat postOffset; // 纬线之间的间距
postOffset = (rect.size.height - axisMarginBottom - axisMarginTop) * 1.0 / ([self.latitudeTitles count] - 1);
CGFloat offset = rect.size.height - axisMarginBottom;
for (int i = 1; i < [self.latitudeTitles count]; i++) {
CGContextMoveToPoint(context, 0, offset - i * postOffset);
CGContextAddLineToPoint(context, rect.size.width , offset - i * postOffset);
}
CGContextStrokePath(context);
//还原线条
CGContextSetLineDash(context, 0, nil, 0);
}
效果图:
绘制Y轴刻度
- (void)drawYAxisTitles:(CGRect)rect {
if ([self.latitudeTitles count] <= 0) {
return;
}
CGFloat postOffset;
postOffset = (rect.size.height - axisMarginBottom - axisMarginTop) * 1.0 / ([self.latitudeTitles count] - 1);
CGFloat offset = rect.size.height - axisMarginBottom;
for (int i = 0; i < [self.latitudeTitles count]; i++) {
// 左侧
// 绘制线条
NSString *valueStr = (NSString *) [self.latitudeTitles objectAtIndex:i];
UIFont *textFont= [UIFont systemFontOfSize:12]; //设置字体
NSMutableParagraphStyle *textStyle=[[NSMutableParagraphStyle alloc]init];//段落样式
textStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attrs = @{NSFontAttributeName:textFont,
NSParagraphStyleAttributeName:textStyle,
NSForegroundColorAttributeName:[UIColor blackColor]};
CGSize textSize = [valueStr boundingRectWithSize:CGSizeMake(100, 100)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attrs
context:nil].size;
/*
显示左边
*/
textStyle.alignment=NSTextAlignmentLeft;
//调整Y轴坐标位置
if (i == [self.latitudeTitles count] - 1) {
CGRect textRect= CGRectMake(axisMarginLeft, offset - i * postOffset, textSize.width, textSize.height);
//绘制字体
[valueStr drawInRect:textRect withAttributes:attrs];
} else {
CGRect textRect= CGRectMake(axisMarginLeft, offset - i * postOffset - textSize.height - 1, textSize.width, textSize.height);
//绘制字体
[valueStr drawInRect:textRect withAttributes:attrs];
}
}
}
效果图:
最后,绘制十字交叉线
绘制十字交叉线是本篇最复杂的一个部分,其实也没有那么吓人。原理就是在用户点击或者平移的时候,显示出触摸点的XY轴信息即可。用户触摸点的XY坐标我们是可以获取到的,拿到了XY坐标就可以算出XY轴具体要显示什么信息。
用户点击屏幕时,我们可以通过 touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
方法中的 touches
获取触摸点的XY坐标。
用户平移时,我们可以通过 touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
中的 touches
获取触摸点的XY坐标。
下面是源代码:
/**
绘制十字交叉线
@param rect 绘制区域
*/
- (void)drawCrossLines:(CGRect)rect {
//过滤非显示区域的点
if (self.singleTouchPoint.x < axisMarginLeft ||
self.singleTouchPoint.y < axisMarginTop ||
self.singleTouchPoint.x > rect.size.width - axisMarginRight ||
self.singleTouchPoint.y > rect.size.height - axisMarginBottom) {
return;
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1.0f);
//设置线条为虚线
CGFloat lengths[] = {2.0, 2.0};
CGContextSetLineDash(context, 0.0, lengths, 1);
// 绘制纵向刻度文字
NSString *valueStr = [self calcAxisXGraduate:rect];
if (![valueStr isEqualToString:@""]) {
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
//绘制纵线
//还原半透明
CGContextSetAlpha(context, 1);
// 移动初始点
CGContextMoveToPoint(context, self.singleTouchPoint.x, 0);
// 添加line
CGContextAddLineToPoint(context, self.singleTouchPoint.x, rect.size.height - axisMarginBottom);
//绘制线条
CGContextStrokePath(context);
// 绘制字体
UIFont *textFont= [UIFont systemFontOfSize:12]; //设置字体
NSMutableParagraphStyle *textStyle=[[NSMutableParagraphStyle alloc]init];//段落样式
textStyle.lineBreakMode = NSLineBreakByWordWrapping;
textStyle.alignment=NSTextAlignmentCenter;
NSDictionary *attrs = @{NSFontAttributeName:textFont,
NSParagraphStyleAttributeName:textStyle,
NSForegroundColorAttributeName:[UIColor whiteColor]};
CGSize textSize = [valueStr boundingRectWithSize:CGSizeMake(100, 100)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attrs
context:nil].size;
CGRect boxRect = CGRectMake(self.singleTouchPoint.x - textSize.width / 2.0, 1, textSize.width, textSize.height);
CGContextAddRect(context,boxRect);
CGContextFillPath(context);
[valueStr drawInRect:boxRect withAttributes:attrs];
}
// 绘制横向刻度文字
NSString *valueStr2 = [self calcAxisYGraduate:rect];
if (![valueStr2 isEqualToString:@""]) {
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
//绘制横线
//还原半透明
CGContextSetAlpha(context, 1);
CGContextMoveToPoint(context, 0, self.singleTouchPoint.y);
CGContextAddLineToPoint(context, rect.size.width, self.singleTouchPoint.y);
//绘制线条
CGContextStrokePath(context);
// 绘制字体
UIFont *textFont2= [UIFont systemFontOfSize:12]; //设置字体
NSMutableParagraphStyle *textStyle2 = [[NSMutableParagraphStyle alloc] init];//段落样式
textStyle2.lineBreakMode = NSLineBreakByWordWrapping;
textStyle2.alignment=NSTextAlignmentLeft;
NSDictionary *attrs2 = @{NSFontAttributeName:textFont2,
NSParagraphStyleAttributeName:textStyle2,
NSForegroundColorAttributeName:[UIColor whiteColor]};
CGSize textSize2 = [valueStr2 boundingRectWithSize:CGSizeMake(100, 100)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attrs2
context:nil].size;
CGRect boxRect2 = CGRectMake(1, self.singleTouchPoint.y - textSize2.height / 2.0, textSize2.width, textSize2.height);
CGContextAddRect(context,boxRect2);
CGContextFillPath(context);
[valueStr2 drawInRect:boxRect2 withAttributes:attrs2];
}
CGContextSetLineDash(context, 0, nil, 0);
}
// 获取十字交叉线的X轴刻度
- (NSString *)calcAxisXGraduate:(CGRect)rect {
return [NSString stringWithFormat:@"%f", [self touchPointAxisXValue:rect]];
}
// 获取十字交叉线的Y轴刻度
- (NSString *)calcAxisYGraduate:(CGRect)rect {
return [NSString stringWithFormat:@"%f", [self touchPointAxisYValue:rect]];
}
// 计算触摸点X坐标值占坐标系宽度比例
- (CGFloat)touchPointAxisXValue:(CGRect)rect {
CGFloat length = rect.size.width - self.axisMarginLeft - self.axisMarginRight;
CGFloat valueLength = self.singleTouchPoint.x - self.axisMarginLeft ;
return valueLength / length;
}
// 计算触摸点Y坐标值占坐标系高度比例
- (CGFloat)touchPointAxisYValue:(CGRect)rect {
CGFloat length = rect.size.height - self.axisMarginBottom - self.axisMarginTop;
CGFloat valueLength = length - (self.singleTouchPoint.y - self.axisMarginTop);
return valueLength / length;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray *allTouches = [touches allObjects];
//处理点击事件
if ([allTouches count] == 1) {
//获取选中点
self.singleTouchPoint = [[allTouches objectAtIndex:0] locationInView:self];
//重绘
[self setNeedsDisplay];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray *allTouches = [touches allObjects];
//处理点击事件
if ([allTouches count] == 1) {
//获取选中点
self.singleTouchPoint = [[allTouches objectAtIndex:0] locationInView:self];
//重绘
[self setNeedsDisplay];
}
}
效果图: