iOS 饼图

261 阅读1分钟
**@interface** PieChartView : UIView

**@property** (**nonatomic**, **strong**) NSArray *dataArray;

**@property** (**nonatomic**, **strong**) NSArray *colorArray;

**@property** (**nonatomic**, **strong**) NSArray *detailTextArray;

**@property** (**nonatomic**, **strong**) NSString *title;


- (**void**)strokePath;


**@end**

**@interface** PieChartView ()

**@property** (**nonatomic**, **strong**) NSString *titleColor;

\


**@property** (**nonatomic**, **strong**) NSArray *proportionArray;

**@property** (**nonatomic**, **strong**) NSString *total;

**@property** (**nonatomic**, **strong**) UIView *centreView;

**@property** (**nonatomic**, **strong**) UILabel *titleLabel;

**@end**

\


**@implementation** PieChartView

\


- (**instancetype**)initWithFrame:(CGRect)frame

{

    **self** = [**super** initWithFrame:frame];

    **if** (**self**) {

        

    }

    

    **return** **self**;

}

\


- (**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 {

    // Drawing code

    

    [**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 array];

    NSMutableArray *centreAngleArray = [NSMutableArray array];

    

    CGFloat current_radius = radius;

    **for** (**int** i = 0; i < **self**.dataArray.count; i++ ) {

        

        UIColor *color = **self**.colorArray[i];

        NSNumber *number = **self**.proportionArray[i];

        //计算弧线的绝对角度和结束角度

        CGFloat angle = [number floatValue]*M_PI*2;

        endAngle = startAngle + angle;

        

        //如果该数据小于上一个数据, 半径递减

        **if** (i != 0 && [number doubleValue] < [**self**.proportionArray[i-1] doubleValue]) {

            current_radius = current_radius - 5;

        }

        //画弧线

        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));

        **if** (centreAngleArray.count) {

            [centreAngleArray addObject:[NSNumber numberWithFloat:centreAngle]];

            [pointArray addObject:[NSValue valueWithCGPoint:centrePoint]];

        } **else** {

            [centreAngleArray insertObject:[NSNumber numberWithFloat:centreAngle] atIndex:0];

            [pointArray insertObject:[NSValue valueWithCGPoint:centrePoint] atIndex:0];

        }

        //该弧线的结束角度为下一个弧线的开始角度

        startAngle = endAngle;

    }

    

    [**self** drawLineWithPointArray:pointArray centerArray:centreAngleArray];

    

    //饼图中间空白区域

    CGFloat centerViewWidth = current_radius-15;

    **self**.centreView.frame = CGRectMake(0, 0, centerViewWidth*2, centerViewWidth*2);

    **self**.centreView.center = centerPoint;

    **self**.centreView.layer.cornerRadius = centerViewWidth;

    **self**.centreView.layer.masksToBounds = **YES**;

    [**self** addSubview:**self**.centreView];

    //标题

    **self**.titleLabel.frame = CGRectMake(0, 0, centerViewWidth*2, centerViewWidth*2);

    **self**.titleLabel.text = [NSString stringWithFormat:@"%@\n%@", **self**.title, **self**.total];

    [**self**.centreView addSubview:**self**.titleLabel];

    

}

/**

 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; i < pointArray.count; i++) {

        //指引线起点

        CGPoint startPoint = [pointArray[i] CGPointValue];

        

        CGFloat radianCenter = [centerArray[i] floatValue];

        

        UIColor *color = **self**.colorArray[i];

        

        NSString *proportionStr = [NSString stringWithFormat:@"%.2f%%", [**self**.proportionArray[i] doubleValue]*100];

        **if** ([**self**.total doubleValue] == 0) {

            proportionStr = @"";

        }

        

        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;

        }

        

        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];

    }

    

}

\


- (UIView *)centreView {

    **if** (!_centreView) {

        _centreView = [[UIView alloc] init];

        _centreView.backgroundColor = **self**.backgroundColor;

    }

    **return** _centreView;

}

\


- (UILabel *)titleLabel {
    **if** (!_titleLabel) {
        _titleLabel = [[UILabel alloc] init];
        _titleLabel.textAlignment = NSTextAlignmentCenter;
        _titleLabel.font = [UIFont systemFontOfSize:12];
        _titleLabel.textColor = [UIColor cyanColor];
    }
    **return** _titleLabel;
}
\