CoreGraphics繪制圖表教程(一)
參考自《Cocoa-Charts》
本篇介紹如何使用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
函數的七個參數:
另外我總結了一下各個角度的位置,如圖:
我們示例中起始弧度是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);
效果圖:
錯誤原因:之所以只填充了一個“月牙”,還是因為自動閉合路徑的緣故。因為沒有把圓心設為起點,所以它閉合的是圓弧的起點和終點。就是這段?。?div id="ssm2444" class="image-package">
一段圓弧
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];
}
}
效果圖:
Github示例源碼
鏈接地址:CoreGraphicsDrawChart
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
【Android 自定義View之繪圖】 基礎圖形的繪制 一、Paint與Canvas 繪圖需要兩個工具,筆和紙。...
寫在前面 ggplot2 是一個功能強大且靈活的R包 ,由Hadley Wickham 編寫,其用于生成優雅的圖...
1 夏夜的凌晨,悶熱逼仄的二樓,窗外樹葉沒有一點動靜,風不知躲到哪兒去了。 我的夢在汗水里起起伏伏,拌著老公的呼嚕...
HTTP狀態碼(HTTP Status Code)是用以表示網頁HTTP響應狀態的3位數字代碼。