Quartz2D繪制網狀圖
引言
做開發也有一段時間了,但是從來沒有自己研究過Quartz2D繪圖,這幾天幫朋友忙,順便研究了一下Quartz2D,做了一個簡單的效果,在這里和大家分享一下, 直接上代碼。
效果圖
畫圓 (繪圖中所與角度相關的都是使用弧度制)
CGFloat width = rect.size.width;
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:rect];
[[UIColor whiteColor] set];
[rectPath fill];
for (int a = 1; a <= 4; a++) {
UIBezierPath *zero = [UIBezierPath bezierPathWithArcCenter:CGPointMake(width / 2, rect.size.height / 2) radius:width / 8 * a startAngle:0 endAngle:M_PI * 2 clockwise:YES];
[[UIColor lightGrayColor] set];
[zero stroke];
}
繪制虛線
CGFloat width = rect.size.width;
NSArray blueArray = @[@0.5, @0.7, @0.8]; // 這里的數組是數據源, 浮點線, 表示技能百分比
NSArray readArray = @[@0.7, @0.85, @0.3];
CGFloat dash[] = {4, 4}; // 虛線長度與間距
for (int a = 0; a <= 2; a++) {
NSNumber blueProportion = blueArray[a];
float blueLength = width / 2 blueProportion.floatValue;
NSNumber readProportion = readArray[a];
float readLength = width / 2 readProportion.floatValue;
CGFloat blueX;
CGFloat blueY;
CGFloat readX;
CGFloat redaY;
UIBezierPath pathLine = [UIBezierPath bezierPath];
[pathLine moveToPoint:CGPointMake(width / 2, width / 2)];
CGFloat x;
CGFloat y;
switch (a) {
case 0:
x = width / 2 - (width / 2 cos(M_PI / 6));
y = width / 2 sin(M_PI / 6) + width / 2;
readX = width / 2 - (readLength cos(M_PI / 6));
redaY = readLength sin(M_PI / 6) + width / 2;
blueX = width / 2 - (blueLength cos(M_PI / 6));
blueY = blueLength sin(M_PI / 6) + width / 2;
break;
case 1:
x = width / 2;
y = 0;
readX = width / 2 ;
redaY = width / 2 - readLength;
blueX = width / 2 ;
blueY = width / 2 - blueLength;
break;
case 2:
x = width / 2 cos(M_PI / 6) + width / 2;
y = width / 2 sin(M_PI / 6) + width / 2;
readX = readLength cos(M_PI / 6) + width / 2;
redaY = readLength sin(M_PI / 6) + width / 2;
blueX = blueLength cos(M_PI / 6) + width / 2;
blueY = blueLength * sin(M_PI / 6) + width / 2;
break;
default:
break;
}
CGPoint point = CGPointMake(x, y);
[pathLine addLineToPoint:point];
[[UIColor lightGrayColor] set];
[pathLine setLineDash:dash count:2 phase:0];
[pathLine stroke];
NSValue *read = [NSValue valueWithCGPoint:CGPointMake(readX, redaY)];
NSValue *blue = [NSValue valueWithCGPoint:CGPointMake(blueX, blueY)];
[self.boolArray addObject:(blueLength > readLength) ? @1 : @0]; // 判斷藍色頂點是否在外面,保存方面后面使用
[self.readPointArray addObject:read];
[self.bluePointArray addObject:blue]; // 保存藍色和紅色頂點方面后面使用
}</code></pre>
繪制虛線會用到這個方法
(void)setLineDash:(nullable const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase;
- pattern:浮點型數組,表示長度間隙值
- count:整形,一般pattern個數,
- phase:偏移量
繪制紅色區域
UIBezierPath *readPathLind = [UIBezierPath bezierPath];
NSValue *one = self.readPointArray[0];
NSValue *two = self.readPointArray[1];
NSValue *three = self.readPointArray[2];
[readPathLind moveToPoint:one.CGPointValue];
[readPathLind addLineToPoint:two.CGPointValue];
[readPathLind addLineToPoint:three.CGPointValue];
[[[UIColor alloc] initWithRed:239.0 / 255 green:76.0 / 255 blue:110 / 255 alpha:1] set];
[readPathLind fill];
繪制藍色重疊區域
for (int idex = 0; idex <= 2; idex++) {
CGFloat width = self.frame.size.width;
CGContextRef inRef = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(inRef, width / 2, width / 2);
NSValue read = self.readPointArray[idex];
NSValue blue = self.bluePointArray[idex];
CGPoint readPoint = read.CGPointValue;
CGPoint bluePoint = blue.CGPointValue;
NSNumber numberOne;
NSNumber numberTow;
NSValue otherRead;
NSValue otherBlue;
switch (idex) {
case 0:
numberOne = self.boolArray[0];
numberTow = self.boolArray[1];
otherRead = self.readPointArray[idex + 1];
otherBlue = self.bluePointArray[idex + 1];
break;
case 1:
numberOne = self.boolArray[1];
numberTow = self.boolArray[2];
otherRead = self.readPointArray[idex + 1];
otherBlue = self.bluePointArray[idex + 1];
break;
case 2:
numberOne = self.boolArray[2];
numberTow = self.boolArray[0];
otherRead = self.readPointArray[0];
otherBlue = self.bluePointArray[0];
break;
default:
break;
}
CGPoint otherReadPoint = otherRead.CGPointValue;
CGPoint otherBluePoint = otherBlue.CGPointValue;
/*
藍色區域如果和紅色有相交區域,則這部分在分塊繪制;
下面是判斷藍色當前頂點和下一個頂點的連線與紅色當前頂點和下一個頂點連線有沒有交點;
單獨一塊中藍色紅色頂點連線如果沒有交點, 則取靠內的兩個頂點,與中心點作為繪圖區域;
如果有交點還是取靠內頂點, 中心點, 外加交點坐標。
:warning::warning::warning: 有交點時一定要注意四個點的順序, 必須是交點在兩個頂點中間加入繪圖路徑
*/
if (numberOne.intValue == numberTow.intValue && numberOne.intValue == 1) { // 判斷當前頂點和下一個頂點是不是都在外面
CGContextAddLineToPoint(inRef, readPoint.x, readPoint.y);
CGContextAddLineToPoint(inRef, otherReadPoint.x, otherReadPoint.y);
} else if (numberOne.intValue == numberTow.intValue && numberOne.intValue == 0) { // 判斷當前頂點和下一個頂點是不是都在里面
CGContextAddLineToPoint(inRef, bluePoint.x, bluePoint.y);
CGContextAddLineToPoint(inRef, otherBluePoint.x, otherBluePoint.y);
} else { // 當前頂點和下一個頂點一個在內一個在外, 藍色頂點和紅色頂點有交點
CGPoint point = [self intersection2:readPoint u2:otherReadPoint v1:bluePoint v2:otherBluePoint]; // 獲取交點
if (numberOne.intValue == 1) { // 判斷當前頂點是不是在外面
CGContextAddLineToPoint(inRef, readPoint.x, readPoint.y);
CGContextAddLineToPoint(inRef, point.x, point.y);
CGContextAddLineToPoint(inRef, otherBluePoint.x, otherBluePoint.y);
} else {
CGContextAddLineToPoint(inRef, bluePoint.x, bluePoint.y);
CGContextAddLineToPoint(inRef, point.x, point.y);
CGContextAddLineToPoint(inRef, otherReadPoint.x, otherReadPoint.y);
}
}
UIColor *color = [[UIColor alloc] initWithRed:45.0 / 255 green:110.0 / 255 blue:250.0 / 255 alpha:1];
UIColor *endColor = [[UIColor alloc] initWithRed:118.0 / 255 green:173.0 / 255 blue:250.0 / 255 alpha:1];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 1.0};
NSArray *array = @[(__bridge id)color.CGColor, (__bridge id)endColor.CGColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)array, locations);
CGContextSaveGState(inRef);
// 剪切路徑, 類似視圖裁剪
CGContextClip(inRef);
CGRect rect = CGContextGetClipBoundingBox(inRef);
// 漸變路徑
CGContextDrawLinearGradient(inRef, gradient, CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect)), CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect)), 0);
CGContextRestoreGState(inRef);
// 注意釋放
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}</code></pre>
添加漸變中會使用CGGradientRef這個玩意,
CGGradientCreateWithColors( CGColorSpaceRef __nullable space, CFArrayRef cg_nullable colors,const CGFloat * __nullable locations)
- space: CGColorSpaceRef實例
- CFArrayRef: 存放顏色的數組@[(__bridge id)color.CGColor……]
- locations:一個浮點型數組, 取之0-1,我理解應該是漸變的速度之類的
繪制藍色非重疊區域
CGContextRef outRef = UIGraphicsGetCurrentContext();
NSMutableArray points = [NSMutableArray array]; // 存放路徑點的數組
// 判斷藍色如果全部在內就不畫外面
if (![self.boolArray containsObject:@1]) {
return;
}
for (int idex = 0; idex <= 2; idex++) {
NSValue read = self.readPointArray[idex];
NSValue blue = self.bluePointArray[idex];
CGPoint readPoint = read.CGPointValue;
CGPoint bluePoint = blue.CGPointValue;
NSNumber numberOne;
NSNumber numberTow;
NSValue otherRead;
NSValue *otherBlue;
switch (idex) {
case 0:
numberOne = self.boolArray[0];
numberTow = self.boolArray[1];
otherRead = self.readPointArray[idex + 1];
otherBlue = self.bluePointArray[idex + 1];
break;
case 1:
numberOne = self.boolArray[1];
numberTow = self.boolArray[2];
otherRead = self.readPointArray[idex + 1];
otherBlue = self.bluePointArray[idex + 1];
break;
case 2:
numberOne = self.boolArray[2];
numberTow = self.boolArray[0];
otherRead = self.readPointArray[0];
otherBlue = self.bluePointArray[0];
break;
default:
break;
}
CGPoint otherReadPoint = otherRead.CGPointValue;
CGPoint otherBluePoint = otherBlue.CGPointValue;
/*
:warning::warning::warning:最煩的地方,自己都快整瘋了
這里沒有使用設置起點, 添加點的方式繪制, 利用的點數組,將需要繪制的點按順序添加至數組利用CGContextAddLines函數繪制,所以點的順序就尤為關鍵
*/
// 如果藍色頂點在外面肯定要添加至數組
if (numberOne.intValue == 1) {
NSValue *blue = [NSValue valueWithCGPoint:bluePoint];
[points addObject:blue];
}
if (numberTow.intValue != numberOne.intValue) {
CGPoint point = [self intersection2:readPoint u2:otherReadPoint v1:bluePoint v2:otherBluePoint];
NSValue *pointV= [NSValue valueWithCGPoint:point];
NSValue *read = [NSValue valueWithCGPoint:readPoint];
NSValue *otherRead = [NSValue valueWithCGPoint:otherReadPoint];
// 交點肯定也要添加至數組
if (numberOne.intValue == 1) { // 藍色頂點先外后內,則先添加交點, 后添加第一個紅色頂點
[points addObject:pointV];
[points addObject:read];
} else { // 藍色頂點先內后外,則先添加第二個紅色頂點,后添加交點
[points addObject:otherRead];
[points addObject:pointV];
}
}
/*
:warning::warning::warning:這里比較繞,最好自己畫圖理解一下, 自己排一下順序
*/
}
NSInteger count = points.count;
CGPoint pointLine[count];
for (int a = 0; a< count; a++) {
NSValue *value = points[a];
CGPoint point = value.CGPointValue;
pointLine[a].x = point.x;
pointLine[a].y = point.y;
}
CGContextAddLines(outRef, pointLine, count);
UIColor color = [[UIColor alloc] initWithRed:45.0 / 255 green:110.0 / 255 blue:250.0 / 255 alpha:1];
UIColor endColor = [[UIColor alloc] initWithRed:118.0 / 255 green:173.0 / 255 blue:250.0 / 255 alpha:1];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 1.0};
CGContextClosePath(outRef);
NSArray *array = @[(bridge id)color.CGColor, (bridge id)endColor.CGColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)array, locations);
CGContextSaveGState(outRef);
CGContextClip(outRef);
CGRect rect = CGContextGetClipBoundingBox(outRef);
CGContextDrawLinearGradient(outRef, gradient, CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect)), CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect)), 0);
CGContextRestoreGState(outRef);
CGGradientRelease(gradient);</code></pre>
繪制白色分割線
CGFloat width = rect.size.width;
for (int a = 0; a <= 2; a++) {
UIBezierPath *pathLine = [UIBezierPath bezierPath];
[pathLine moveToPoint:CGPointMake(width / 2, width / 2)];
NSNumber *numberOne = self.boolArray[a];
NSValue *value;
if (numberOne.integerValue > 0) {
value = self.bluePointArray[a];
} else {
value = self.readPointArray[a];
}
CGPoint point = value.CGPointValue;
[pathLine addLineToPoint:point];
[[UIColor whiteColor] setStroke];
[pathLine strokeWithBlendMode:kCGBlendModeNormal alpha:0.1];
}
換一組數據看看效果

效果圖
NSArray *blueArray = @[@0.9, @0.7, @0.8];
NSArray *readArray = @[@0.7, @0.85, @0.3];
提醒:繪制圓和繪制虛線順序不能顛倒,不然會導致虛線看不見
來自:http://www.jianshu.com/p/e33244d96430