
PieChartView *pieChartView = [[PieChartView alloc] initWithFrame:CGRectMake(0, 0, 300, 500)];
pieChartView.backgroundColor = [UIColor whiteColor];
pieChartView.dataArray = @[@"120", @"150", @"432", @"500", @"100", @"300"];
pieChartView.detailTextArray = @[@"数据1", @"数据2", @"数据3", @"数据4", @"数据5", @"数据6"];
pieChartView.colorArray = @[
[UIColor colorWithRed:251/255.0 green:166.9/255.0 blue:96.5/255.0 alpha:1],
[UIColor colorWithRed:151.9/255.0 green:188/255.0 blue:95.8/255.0 alpha:1],
[UIColor colorWithRed:245/255.0 green:94/255.0 blue:102/255.0 alpha:1],
[UIColor colorWithRed:29/255.0 green:140/255.0 blue:140/255.0 alpha:1],
[UIColor colorWithRed:121/255.0 green:113/255.0 blue:199/255.0 alpha:1],
[UIColor colorWithRed:16/255.0 green:149/255.0 blue:224/255.0 alpha:1]
];
[self.view addSubview:pieChartView];
[pieChartView strokePath];
@interface PieChartView : UIView
@property (nonatomic, strong) NSArray *dataArray;
@property (nonatomic, strong) NSArray *colorArray;
@property (nonatomic, strong) NSArray *detailTextArray;
-(void)strokePath;
@end
@interface PieChartView ()
@property (nonatomic, strong) NSArray *proportionArray
@property (nonatomic, strong) NSString *total
@end
@implementation PieChartView
// 计算数据总和以及各部分的比例
-(void)handleData {
self.total = [NSString stringWithFormat:@"%.2f", [[self.dataArray valueForKeyPath:@"@sum.floatValue"] floatValue]]
NSMutableArray *tempArray = [NSMutableArray array]
for (NSNumber *number in self.dataArray) {
CGFloat numberProportion = [number floatValue]/[self.total floatValue]
[tempArray addObject:[NSNumber numberWithFloat:numberProportion]]
}
self.proportionArray = [NSArray arrayWithArray:tempArray]
}
-(void)strokePath {
[self handleData]
CGFloat startAngle = -M_PI_2
CGFloat endAngle = -M_PI_2
CGFloat radius = self.frame.size.width/5
CGPoint centerPoint = CGPointMake(self.frame.size.width/2 , self.frame.size.height/2)
//用来保存每段弧线夹角的中间角度和弧线中间点
NSMutableArray *pointArray = [[NSMutableArray alloc] init]
NSMutableArray *centreAngleArray = [[NSMutableArray alloc] init]
CGFloat current_radius = radius
for (int i = 0
UIColor *color = self.colorArray[i]
NSNumber *number = self.proportionArray[i]
//计算每一部分的弧线的绝对角度和结束角度
CGFloat absoluteAngle = [number floatValue]*M_PI*2
endAngle = startAngle + absoluteAngle
// 每一部分的半径递减
if (i != 0) {
current_radius = current_radius - 10
}
//画弧线
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:centerPoint radius:current_radius startAngle:startAngle endAngle:endAngle clockwise:**YES**]
[path addLineToPoint:centerPoint]
CAShapeLayer *pieLayer = [CAShapeLayer layer]
[self.layer addSublayer:pieLayer]
pieLayer.fillColor = color.CGColor
pieLayer.strokeColor = color.CGColor
pieLayer.path = path.CGPath
//计算每一部分的弧线的中间角度和弧线中间点,并保存
CGFloat centreAngle = (startAngle + endAngle)/2
CGPoint centrePoint = CGPointMake(centerPoint.x + current_radius*cos(centreAngle), centerPoint.y + current_radius*sin(centreAngle))
[centreAngleArray addObject:[NSNumber numberWithFloat:centreAngle]]
[pointArray addObject:[NSValue valueWithCGPoint:centrePoint]]
//记录该弧线的结束角度,它是下一个弧线的开始角度
startAngle = endAngle
}
// 添加指引线
[self drawLineWithPointArray:pointArray centerArray:centreAngleArray]
}
// 添加指引线方法: pointArray 指引线在饼图扇区的起点 centerArray 起点的角度
-(void)drawLineWithPointArray:(NSArray *)pointArray centerArray:(NSArray *)centerArray {
//记录每一个detail文字的frame
CGRect rect = CGRectZero
CGFloat width = self.bounds.size.width*0.5
for (int i = 0
UIColor *color = self.colorArray[i]
//指引线起点和角度
CGPoint startPoint = [pointArray[i] CGPointValue]
CGFloat radianCenter = [centerArray[i] floatValue]
// 计算各部分数组占比,以及文字
NSString *proportionStr = [NSString stringWithFormat:@"%.2f%%", [self.proportionArray[i] doubleValue]*100]
NSString *text = [NSString stringWithFormat:@"%@\n%@", self.detailTextArray[i], proportionStr]
//指引线转折点
CGFloat turnPointTmp = 5 + i*5
CGFloat turnPoint_X = startPoint.x + turnPointTmp *cos(radianCenter)
CGFloat turnPoint_Y = startPoint.y + turnPointTmp *sin(radianCenter)
//指引线终点
CGFloat endPoint_X = 0
CGFloat endPoint_Y = 0
//文字的frame数据
CGFloat titleWidth = 60
CGFloat titleHeight = 30
CGFloat title_X = 0
CGFloat title_Y = turnPoint_Y
// 文字的属性
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init]
paragraph.alignment = NSTextAlignmentLeft
// 每一部分弧线的中间点在左边或右边,文字和指引线结束点不同
if (startPoint.x <= width) {// 左边
endPoint_X = titleWidth + 15
endPoint_Y = turnPoint_Y
title_X = endPoint_X - titleWidth -7
paragraph.alignment = NSTextAlignmentRight
} else {//在右边
endPoint_X = width*2 - (titleWidth + 15)
endPoint_Y = turnPoint_Y
title_X = endPoint_X + 7
}
// 文字的左边 y 值
title_Y = endPoint_Y - titleHeight/2
// 修正 文字 可能重叠的情况
//排除右边从上到下第一个(i == 0)和左边从下到上第一个(startPoint.x <= width && CGRectGetMidX(rect)> width)
if (i != 0 && !(startPoint.x <= width && CGRectGetMidX(rect)> width)) {
CGRect rect1 = CGRectMake(title_X, title_Y, titleWidth, titleHeight)
CGFloat margin = 0
if (CGRectIntersectsRect(rect, rect1)) {
/**两个面积是否重叠,有三种情况 1,压在上面 2、压在下面 3、包含*/
if (CGRectContainsRect(rect, rect1)) {//包含
if (startPoint.x <= width) {//左边
margin = CGRectGetMaxY(rect1) -rect.origin.y
endPoint_Y -= margin
} else {//右边
margin = CGRectGetMaxY(rect) -rect1.origin.y
endPoint_Y += margin
}
} else {//相交
if (CGRectGetMaxY(rect1) > rect.origin.y && rect1.origin.y < rect.origin.y) {//rect1下半部分 与rect上半部分重叠
if (startPoint.x <= width) {//左边
margin = CGRectGetMaxY(rect1) - rect.origin.y
endPoint_Y -= margin
} else {//右边
margin = CGRectGetMaxY(rect) -rect1.origin.y
endPoint_Y += margin
}
} else if (rect1.origin.y < CGRectGetMaxY(rect) && CGRectGetMaxY(rect1) > CGRectGetMaxY(rect)) {//rect1上半部分 与rect下半部分重叠
if (startPoint.x <= width) {//左边
margin = CGRectGetMaxY(rect1) - rect.origin.y
endPoint_Y -= margin
} else {//右边
margin = CGRectGetMaxY(rect) -rect1.origin.y
endPoint_Y += margin
}
}
}
} else {//相离
if (startPoint.x <= width) {//左边
if (rect1.origin.y > CGRectGetMaxY(rect)) {//rect1靠下,rect靠上
margin = CGRectGetMaxY(rect1) - rect.origin.y
endPoint_Y -= margin
}
} else {//右边
if (CGRectGetMaxY(rect1) < rect.origin.y) {//rect靠下,rect1靠上
margin = CGRectGetMaxY(rect) -rect1.origin.y
endPoint_Y += margin
}
}
}
/**超出页面上下的情况 */
if (startPoint.x <= width) {//左边
if (endPoint_Y <= 0) {//
endPoint_Y = 0
endPoint_X = CGRectGetMaxX(rect)
}
} else {//右边
if (endPoint_Y+titleHeight >= **self**.frame.size.height) {
endPoint_Y = **self**.frame.size.height - titleHeight
endPoint_X = rect.origin.x-titleWidth
}
}
title_Y = endPoint_Y - titleHeight/2
rect = CGRectMake(title_X, title_Y, titleWidth, titleHeight)
} else {
rect = CGRectMake(title_X, title_Y, titleWidth, titleHeight)
}
//画指引线
UIBezierPath *path = [UIBezierPath bezierPath]
[path moveToPoint:CGPointMake(endPoint_X, endPoint_Y)]
[path addLineToPoint:CGPointMake(turnPoint_X, turnPoint_Y)]
[path addLineToPoint:startPoint]
CAShapeLayer *pieLayer = [CAShapeLayer layer]
[self.layer addSublayer:pieLayer]
pieLayer.fillColor = [UIColor clearColor].CGColor
pieLayer.strokeColor = color.CGColor
pieLayer.lineWidth = 1
pieLayer.path = path.CGPath
//小圆点
CGFloat dotRadius = 3
UIView *dotView = [[UIView alloc] initWithFrame:CGRectMake(endPoint_X-dotRadius, endPoint_Y-dotRadius, dotRadius*2, dotRadius*2)]
dotView.backgroundColor = color
dotView.layer.cornerRadius = dotRadius
dotView.layer.masksToBounds = **YES**
[self addSubview:dotView]
//画指示文字
NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:12],
NSForegroundColorAttributeName: color,
NSParagraphStyleAttributeName: paragraph}
UILabel *detailLabel = [[UILabel alloc] initWithFrame:CGRectMake(title_X, title_Y, titleWidth, titleHeight)]
detailLabel.attributedText = [[NSAttributedString alloc] initWithString:text attributes:attributes]
detailLabel.numberOfLines = 2
[self addSubview:detailLabel]
}
}