//
// SHDrawStatisticPictureView.h
// testDrawPicture
//
// Created by vanmr on 2019/9/11.
// Copyright © 2019 vanmr. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface SHDrawStatisticPictureView : UIView
///饼图或者柱状图颜色
@property (nonatomic,copy) NSArray <UIColor *>*colors;
/// 初始化
- (instancetype)initWithScoreArray:(NSArray *)scores;
/****************************柱状图***********************/
@property(nonatomic, assign) CGFloat topSpace;
/// 最左边的柱状图距离图表左边框的距离
@property(nonatomic, assign) CGFloat leftSpace;
/// 最右边的柱状图距离图表右边框的距离
@property (nonatomic, assign) CGFloat rightSpace;
/// 坐标系Y 一个刻度的表示的值
@property (nonatomic, assign) NSInteger unitValue;
/// 坐标系Y 总刻度数
@property (nonatomic,assign) CGFloat units;
/// 标尺宽度
@property (nonatomic, assign) CGFloat scaleplateWidth;
/// 图表底部区域的高度
@property (nonatomic,assign) CGFloat chartBottomZoneHeight;
/// 底部区域显示标题集合
@property (nonatomic, copy) NSArray <NSString *>*titles;
/// 底部区域显示的文本颜色
@property (nonatomic, copy) NSArray <UIColor *>*bottomTitleColor;
/// 底部区域默认显示文本颜色
@property (nonatomic, copy) UIColor *defaultTitleColor;
/// 底部字体大小
@property (nonatomic, assign) CGFloat titleFontSize;
/// 坐标系颜色
@property (nonatomic, strong) UIColor *coordinateColor;
/**
* 画条形图
* barWidth: 条码宽度
*/
- (void)startDrawBarCharPicture:(CGFloat)barWidth;
/****************************饼图***********************/
/// 中间小圆的半径(饼图)
@property (nonatomic, assign) CGFloat centerRadius;
/// 分割线颜色
@property (nonatomic, strong) UIColor *separateLineColor;
/// 中间圆的背景色
@property (nonatomic, strong) UIColor *centerCircleBackgroundColor;
/**
* 画饼图
* radius: 半径
*/
- (void)startDrawPieChartPicture:(CGFloat)radius;
@end
NS_ASSUME_NONNULL_END
//
// SHDrawStatisticPictureView.m
// testDrawPicture
//
// Created by vanmr on 2019/9/11.
// Copyright © 2019 vanmr. All rights reserved.
//
#import "SHDrawStatisticPictureView.h"
#define defaultFillColor [UIColor purpleColor]
#define defaultBackgroundColor [UIColor whiteColor]
@interface SHDrawStatisticPictureView ()
@property (nonatomic, strong) NSArray *scores;
@property (nonatomic,strong) NSArray *percentStrings;
@property (nonatomic, assign) CGFloat totalScore;
/// 整个饼图的半径
@property (nonatomic, assign) CGFloat pieRadius;
@property (nonatomic, assign) CGPoint barChartOriginPoint;
/// 图表显示的区域
@property (nonatomic, assign) CGSize barChartSize;
@property (nonatomic, assign) CGFloat unitHeight;
/// 当前颜色索引
@property (nonatomic, assign) NSUInteger currentColorIndex;
/// 当前底部标题的索引
@property (nonatomic, assign) NSUInteger currentTitleIndex;
/// 底部一个title单位的宽度
@property (nonatomic, assign) CGFloat bottomTitleUnitWidth;
@end
@implementation SHDrawStatisticPictureView
- (instancetype)initWithScoreArray:(NSArray *)scores{
if (self = [super init]) {
self.scores = scores;
self.currentColorIndex = 0;
self.currentTitleIndex = 0;
self.backgroundColor = defaultBackgroundColor;
}
return self;
}
- (void)handleScoreData:(NSArray *)scores{
if (!scores.count) {
return;
}
__block CGFloat totalScore = 0;
[self.scores enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
totalScore += [obj floatValue];
}];
self.totalScore = totalScore;
NSMutableArray *percnetValueArr = [NSMutableArray array];
__block int totalScoreExceptLastValue = 0;
__block int totalScoreValue = 0;
[self.scores enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
int value = (int)([obj floatValue]/totalScore * 100 + 0.5);
totalScoreValue += value;
if (idx < self.scores.count - 1) {
totalScoreExceptLastValue += value;
}else{
value = totalScoreValue - totalScoreExceptLastValue;
}
NSString *percentString = percentString = [NSString stringWithFormat:@"%d%%",value];
[percnetValueArr addObject:percentString];
}];
self.percentStrings = percnetValueArr;
}
#pragma mark - 开始画柱状图 ---
- (void)startDrawBarCharPicture:(CGFloat)barWidth{
if (!self.scores.count) {
return;
}
if (CGRectEqualToRect(self.frame, CGRectZero)) {
return;
}
if (barWidth > self.frame.size.width) {
return;
}
[self handleBarCharData];
// 坐标系
[self drawCoordinateSystem];
// 柱状图总个数(不包含间距)
__block CGFloat totalBarCount = 0;
[self.scores enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSArray class]] && obj) {
NSArray *arr = (NSArray *)obj;
[arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (![obj isKindOfClass:[NSArray class]] && obj) {
totalBarCount += 1;
}
}];
}else{
totalBarCount += 1;
}
}];
self.bottomTitleUnitWidth = (self.barChartSize.width - self.leftSpace - self.rightSpace)/self.scores.count - 10.0;
// 一个数值对应的高度
CGFloat valueHeight = self.unitHeight / self.unitValue;
// 坐标原点
CGFloat originPointX = self.scaleplateWidth;
CGFloat originPointY = self.frame.size.height - self.chartBottomZoneHeight;
// 柱与柱之间的间距
CGFloat barSpace = (self.barChartSize.width - self.leftSpace - self.rightSpace - totalBarCount * barWidth)/(self.scores.count - 1);
// 每一个柱状图的中心点
CGFloat pointX = originPointX + self.leftSpace;
// 一组柱状图底部的中心点
CGFloat barGroupCenterX = 0;
for (int i = 0; i<self.scores.count; i++) {
id element = self.scores[i];
if ([element isKindOfClass:[NSArray class]]) {
NSArray *elementArr = (NSArray *)element;
if (!elementArr.count) {
continue;
}
CGFloat groupWidth = elementArr.count * barWidth; // 一组的宽度
for (int j = 0; j <elementArr.count; j++) {
@autoreleasepool {
if (i == 0) {
if (j == 0) {
pointX += 0.5 *barWidth;
}else{
pointX += barWidth;
}
}else{
if (j == 0) {
pointX += (barWidth + barSpace);
}else{
pointX += barWidth;
}
}
CGPoint beginPoint = CGPointMake(pointX, originPointY);
[self drawOneBarPictureSize:CGSizeMake(barWidth, self.barChartSize.height - [elementArr[j] floatValue] *valueHeight) beginPoint:beginPoint];
}
}
// 当元素为数组和非数组元素混合时,这里还有问题
barGroupCenterX = (pointX + 0.5 *barWidth) - 0.5 *groupWidth;
}else{
@autoreleasepool {
if (i == 0) {
pointX += 0.5 *barWidth;
}else{
pointX += (barSpace + barWidth);
}
CGPoint beginPoint = CGPointMake(pointX, originPointY);
[self drawOneBarPictureSize:CGSizeMake(barWidth, self.barChartSize.height - [self.scores[i] floatValue] *valueHeight) beginPoint:beginPoint];
barGroupCenterX = pointX;
}
}
// 图表底部区域
[self drawChartBottomZone:CGPointMake(barGroupCenterX, originPointY)];
}
}
- (void)drawOneBarPictureSize:(CGSize)barSize beginPoint:(CGPoint)beginPoint{
//创建出贝塞尔曲线
UIBezierPath*circlePath = [UIBezierPath bezierPath];
[circlePath moveToPoint:beginPoint];
[circlePath addLineToPoint:CGPointMake(beginPoint.x, barSize.height)];
[self drawOneBarCharPicture:barSize.width path:circlePath];
}
- (void)handleBarCharData{
if (self.units <= 0) {
return;
}
// 坐标原点
CGFloat originPointX = self.scaleplateWidth;
CGFloat originPointY = self.frame.size.height - self.chartBottomZoneHeight;
self.barChartOriginPoint = CGPointMake(originPointX, originPointY);
// 计算图表区
CGFloat width = self.frame.size.width - self.scaleplateWidth;
CGFloat height = self.frame.size.height - self.topSpace - self.chartBottomZoneHeight;
self.barChartSize = CGSizeMake(width, height);
// 计算每一个标尺的高度
self.unitHeight = height / self.units;
}
#pragma mark - 柱状图底部区域绘制 ----
- (void)drawChartBottomZone:(CGPoint)ratePoint{
UIColor *color = [UIColor blackColor];
if (!self.defaultTitleColor) {
if (self.bottomTitleColor.count && self.currentTitleIndex < self.bottomTitleColor.count) {
color = self.bottomTitleColor[self.currentTitleIndex];
}
}else{
color = self.defaultTitleColor;
}
if (self.titles.count && self.currentTitleIndex < self.titles.count) {
NSString *title = self.titles[self.currentTitleIndex];
if (title.length > 0) {
CGFloat fontSize = 14.0;
if (self.titleFontSize > 0) {
fontSize = self.titleFontSize;
}
CATextLayer *textLayer = [[CATextLayer alloc] init];
textLayer.wrapped = YES;
textLayer.alignmentMode = kCAAlignmentCenter;
NSDictionary *attributeDict = @{NSFontAttributeName:[UIFont systemFontOfSize:fontSize],
NSForegroundColorAttributeName:color
};
NSAttributedString *text = [[NSAttributedString alloc] initWithString:title attributes:attributeDict];
textLayer.string = text;
CGFloat textLayerW = self.bottomTitleUnitWidth;
CGFloat textLayerX = ratePoint.x - textLayerW*0.5;
CGFloat textLayerY = ratePoint.y;
CGFloat texlLayerH = self.chartBottomZoneHeight;
textLayer.frame = CGRectMake(textLayerX, textLayerY, textLayerW, texlLayerH);
[self.layer addSublayer:textLayer];
}
}
self.currentTitleIndex++;
}
#pragma mark - 画坐标系 ---
- (void)drawCoordinateSystem{
if (self.units <= 0) {
return;
}
UIColor *coordinateColor = [UIColor redColor];
if (self.coordinateColor) {
coordinateColor = self.coordinateColor;
}
CAShapeLayer *coordinateLayer = [[CAShapeLayer alloc] init];
coordinateLayer.strokeColor = coordinateColor.CGColor;
coordinateLayer.lineWidth = 1.0;
UIBezierPath *path = [UIBezierPath bezierPath];
for (int i = 0; i<self.units + 1; i++) {
@autoreleasepool {
[path moveToPoint:CGPointMake(self.barChartOriginPoint.x - 10.0, self.barChartOriginPoint.y - i *self.unitHeight)];
[path addLineToPoint:CGPointMake(self.barChartOriginPoint.x - 10.0 + self.barChartSize.width, self.barChartOriginPoint.y - i *self.unitHeight)];
CATextLayer *scaleplateTextLayer = [[CATextLayer alloc] init];
NSDictionary *attributeDict = @{NSFontAttributeName:[UIFont systemFontOfSize:14.0],
NSForegroundColorAttributeName:[UIColor blackColor]
};
NSAttributedString *text = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%ld",self.unitValue * i] attributes:attributeDict];
scaleplateTextLayer.string = text;
scaleplateTextLayer.frame = CGRectMake(0, self.barChartOriginPoint.y - i*self.unitHeight - 14.0, 20.0, 14.0);
[self.layer addSublayer:scaleplateTextLayer];
}
}
[path moveToPoint:CGPointMake(self.barChartOriginPoint.x, self.barChartOriginPoint.y)];
[path addLineToPoint:CGPointMake(self.barChartOriginPoint.x, self.topSpace)];
coordinateLayer.path = path.CGPath;
[self.layer addSublayer:coordinateLayer];
}
- (CAShapeLayer *)drawOneBarCharPicture:(CGFloat)barWidth path:(UIBezierPath *)path{
UIColor *strokeColor = defaultFillColor;
if (self.colors.count && self.currentColorIndex < self.colors.count) {
UIColor *color = self.colors[self.currentColorIndex];
if (color) {
strokeColor = color;
}
}
CAShapeLayer *shapeLayer= [CAShapeLayer layer];
shapeLayer.lineWidth=barWidth;
shapeLayer.strokeColor= strokeColor.CGColor;
shapeLayer.path= path.CGPath;
[self.layer addSublayer:shapeLayer];
self.currentColorIndex++;
return shapeLayer;
}
#pragma mark - 开始画饼图 ---
- (void)startDrawPieChartPicture:(CGFloat)radius{
if (!self.scores.count) {
return;
}
if (CGRectEqualToRect(self.frame, CGRectZero)) {
return;
}
if (radius > self.frame.size.width || radius > self.frame.size.height) {
return;
}
[self handleScoreData:self.scores];
self.pieRadius = radius;
CGFloat startA = 0;
CGFloat endA = 0;
for (int i = 0; i < self.scores.count; i++) {
@autoreleasepool {
CGFloat score = [self.scores[i] floatValue];
if (score > 0) {
CGFloat angle = M_PI * 2 * score/self.totalScore;
endA += angle;
[self drawPieChartPartPicture:radius startAngle:startA endAngle:endA percentage:self.percentStrings[i]];
startA = endA;
}
}
}
[self addCenterCircleToLayer];
}
#pragma mark - 画中间圆 ----
- (void)addCenterCircleToLayer{
CAShapeLayer *centerCircleLayer = [[CAShapeLayer alloc] init];
centerCircleLayer.fillColor = defaultBackgroundColor.CGColor;
if (self.centerCircleBackgroundColor) {
centerCircleLayer.fillColor = self.centerCircleBackgroundColor.CGColor;
}
CGFloat centerRadius = self.pieRadius * 0.4;
if (self.centerRadius > 0) {
centerRadius = self.centerRadius;
}
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5) radius:centerRadius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
centerCircleLayer.path = path.CGPath;
[self.layer addSublayer:centerCircleLayer];
}
- (void)drawPieChartPartPicture:(CGFloat)radius startAngle:(CGFloat)startA endAngle:(CGFloat)endA percentage:(NSString *)percentage{
UIColor *fillColor = defaultFillColor;
if (self.colors.count && self.currentColorIndex < self.colors.count) {
UIColor *color = self.colors[self.currentColorIndex];
if (color) {
fillColor = color;
}
}
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.strokeColor = defaultBackgroundColor.CGColor;
if (self.separateLineColor) {
shapeLayer.strokeColor = self.separateLineColor.CGColor;
}
shapeLayer.fillColor = fillColor.CGColor;
shapeLayer.lineWidth = 5.0;
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5) radius:radius startAngle:startA endAngle:endA clockwise:YES];
[path addLineToPoint:CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5)];
[path closePath];
shapeLayer.path = path.CGPath;
[self.layer addSublayer:shapeLayer];
CAShapeLayer *lineLayer = [[CAShapeLayer alloc] init];
lineLayer.strokeColor = fillColor.CGColor;
lineLayer.fillColor = [UIColor clearColor].CGColor;
lineLayer.lineWidth = 2.0;
UIBezierPath *linePath = [UIBezierPath bezierPath];
CGFloat resultA = (endA - startA)*0.5 + startA;
// 扇形中心位置的坐标
CGFloat anglePointX = radius * cosf(resultA) + self.frame.size.width * 0.5;
CGFloat anglePointY = radius * sinf(resultA) + self.frame.size.height * 0.5;
[linePath moveToPoint:CGPointMake(anglePointX, anglePointY)];
NSDictionary *attributeDict = @{ NSFontAttributeName:[UIFont systemFontOfSize:16.0],
NSForegroundColorAttributeName:[UIColor blackColor]
};
NSAttributedString *percentageAttributeString = [[NSAttributedString alloc] initWithString:percentage attributes:attributeDict];
CATextLayer *textlayer = [[CATextLayer alloc] init];
textlayer.string = percentageAttributeString;
CGFloat slopeLineAngle = 1/3.0 * M_PI;
CGFloat addX = 20.0 * cosf(slopeLineAngle);
CGFloat addY = 20.0 * sinf(slopeLineAngle);
CGFloat horizontalLineLength = 30.0;
CGPoint onePoint = CGPointZero;
CGPoint twoPoint = CGPointZero;
if (resultA < M_PI_2 && resultA > 0) { // 左下区间
onePoint = CGPointMake(addX + anglePointX, addY + anglePointY);
twoPoint = CGPointMake(onePoint.x + horizontalLineLength, onePoint.y);
textlayer.frame = CGRectMake(twoPoint.x + 5.0, twoPoint.y - 15.0, 60, 44);
}
if (resultA < M_PI && resultA >= M_PI_2) { // 右下区间
onePoint = CGPointMake(anglePointX - addX, addY + anglePointY);
twoPoint = CGPointMake(onePoint.x - horizontalLineLength, onePoint.y);
textlayer.frame = CGRectMake(twoPoint.x - 38.0, twoPoint.y - 15.0, 60, 44);
}
if (resultA < M_PI * 1.5 && resultA >= M_PI) { // 右上区间
onePoint = CGPointMake(anglePointX - horizontalLineLength, anglePointY);
twoPoint = CGPointMake(onePoint.x - addX, onePoint.y - addY);
textlayer.frame = CGRectMake(twoPoint.x - 10.0, twoPoint.y - 20.0, 60, 44);
}
if (resultA < M_PI * 2.0 && resultA >= M_PI * 1.5) { // 左上区间
onePoint = CGPointMake(anglePointX + horizontalLineLength, anglePointY);
twoPoint = CGPointMake(onePoint.x + addX, onePoint.y - addY);
textlayer.frame = CGRectMake(twoPoint.x - 10.0, twoPoint.y - 20.0, 60, 44);
}
[self.layer addSublayer:textlayer];
[linePath addLineToPoint:onePoint];
[linePath addLineToPoint:twoPoint];
lineLayer.path = linePath.CGPath;
[self.layer addSublayer:lineLayer];
self.currentColorIndex++;
}
@end