How to Alligned text on Piechart (or) piechart slice center?

早过忘川 提交于 2019-12-12 09:29:54

问题


I want add Text in Piechart.

UIGraphicsPushContext(context);
CGContextMoveToPoint(context, newCenterX, newCenterY);
CGContextAddArc(context, newCenterX, newCenterY, radius,arcOffset-(myAngle/2),arcOffset+(myAngle/2), 0);
UIColor *color = [PIECHART_COLORS objectAtIndex:i%6];
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextClosePath(context);
CGContextFillPath(context);
CGMutablePathRef arcPath = CGPathCreateMutable();
CGPathAddArc(arcPath, NULL, self.frame.size.width/2, self.frame.size.height/2, radius, arcOffset-(myAngle/2), arcOffset+(myAngle/2),0);
CGRect rect =CGPathGetBoundingBox(arcPath);

rect = CGRectMake(rect.origin.x+rect.size.width/2, rect.origin.y+rect.size.height/2, 80, 20);

// For InnerCircle Empty

CGContextMoveToPoint(context, newCenterX, newCenterY);
float innercircleRadius = IS_IPAD ? 70 : 40;
CGContextAddArc(context, newCenterX, newCenterY, radius-innercircleRadius,arcOffset-(myAngle/2),arcOffset+(myAngle/2), 0);
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextClosePath(context);
CGContextFillPath(context);

From above I will able to draw PieChart with Inner part White space but I am unable to add text in particular piechart slice. Can anyone help with some good example?

My pie chart is displaying perfectly but text align is not coming in proper way.

//For Text I will Try This Code

NSString *text = [NSString stringWithFormat:@"%.2f %%",[[[myArray objectAtIndex:i] objectForKey:@"Count"] floatValue]];
[text drawInRect: rect withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16], NSForegroundColorAttributeName:[UIColor redColor]} ];

回答1:


Although your question is quite unclear that how exactly you want to center the label to pie slice. I tried with what I understand from the question you posted above.

First thing is use UIBezierPath instead of CGContext. I used Parametric equaction of circle to calculate the boundary points of my circle.

CGPoint newPoints = CGPointMake(a + r * cos(theta), b + r * sin(theta));
// theta = angle
// r = radius
// a = centerX
// b = centerY

It will give you the points where you need to show your label. Now you just need to calculate the correct angle (center of slice) to achieve your goal.

UIBezierPath has a method [bezierPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; which creates an arc.

Now I'm going to share the code step by step:

Two macros:

#define TO_DEGREE(x) (x * 180.0)/M_PI
#define TO_RADIAN(x) (x * M_PI)/180.0

viewDidLoad: method to initialize the dummy data for my chart.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    chartView.backgroundColor = [UIColor clearColor];

    /// Assuming dummy data for our chartView
    NSArray *gendersData = @[@{@"color": [UIColor redColor], @"title": @"Boys", @"percentage": @40},
                         @{@"color": [UIColor greenColor], @"title": @"Girls", @"percentage": @45},
                         @{@"color": [UIColor purpleColor], @"title": @"Unknown", @"percentage": @15}];

    [self preparePieChartForData:gendersData];

}

preparePieChartForData: method

- (void)preparePieChartForData:(NSArray *)data {

    /// We will use Parametric equaction of circle to get the boundary of circle
    /*
     * Parametric equation of circle
     * x = a + r cos t
     * y = b + r sin ⁡t
     * a, b are center of circle
     * t (theta) is angle
     * x and y will be points which are on circumference of circle
     *
               270
                |
            _   |   _
                |
     180 -------o------- 360
                |
            +   |   +
                |
                90
     *
     */

    /// Thats why starting from 270.0
    CGFloat lastAngle = 270.0;
    for (NSDictionary *genderData in data) {

        /// Getting data from dictionary
        CGFloat percentage = [genderData[@"percentage"] floatValue];
        UIColor *color = genderData[@"color"];
        NSString *title = genderData[@"title"];

        /// Calculating the angle from percentage, 360 is full circle.
        CGFloat angle = lastAngle + (360 * percentage)/100.0;
        [self makeSliceStartFrom:lastAngle endAt:angle radius:80.0 color: color title:title];

        /// Updating lastAngle so that next angle can start just after this
        lastAngle = angle;
    }
}

Making pie slice method

- (void)makeSliceStartFrom:(CGFloat)startAngle
                     endAt:(CGFloat)endAngle
                    radius:(CGFloat)radius
                     color:(UIColor *)color
                     title:(NSString *)title {

    /// Converting degree to radians as bezierPath accept angle in radians
    CGFloat endAngleInRadians   = TO_RADIAN(endAngle);
    CGFloat startAngleInRadians = TO_RADIAN(startAngle);
    if (endAngle >= -180.0 && endAngle < -90.0) {
        /// This is because of above diagram
        startAngleInRadians = TO_RADIAN(-90);
    }

    /// This is the center of chartView
    CGPoint center = CGPointMake(chartView.bounds.size.width/2.0, chartView.bounds.size.height/2.0);

    /// Initializing Bezeir path
    UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    [bezierPath addArcWithCenter:center radius:radius startAngle:startAngleInRadians endAngle:endAngleInRadians clockwise:YES];

    /// Line width of pie chart
    CGFloat lineWidth = 30.0;
    CGPathRef slicePath = bezierPath.CGPath;

    /// Making shape layer from the path
    CAShapeLayer *sliceLayer = [CAShapeLayer layer];
    sliceLayer.path = slicePath;
    sliceLayer.strokeColor = color.CGColor;
    sliceLayer.lineWidth = lineWidth;
    sliceLayer.fillColor = [UIColor clearColor].CGColor;
    [chartView.layer addSublayer:sliceLayer];

    /*
     * ------------- LABEL PART -------------
     * Adding label at center of the slice
     */

    /// Creating an empty label
    UILabel *lbl = [[UILabel alloc] init];
    lbl.font = [UIFont systemFontOfSize:10.0];
    lbl.text = title;
    [lbl sizeToFit];

    /// theta is the center (middle) angle of this slice
    CGFloat theta = startAngleInRadians + (endAngleInRadians - startAngleInRadians)/2.0;

    /// Adding lineWith and 10.0 extra in radius so that label can visible outside of the circle
    CGFloat r = radius + lineWidth + 10.0;
    CGFloat a = center.x;
    CGFloat b = center.y;

    /// Calculating points from theta and angle by using parametric equation of cricle
    CGPoint newPoints = CGPointMake(a + r * cos(theta), b + r * sin(theta));
    CGRect  frame = lbl.frame;

    /// Recalculating the origin so that label can be exact center of slice. newPoints are (0, 0) position of label.
    frame.origin = CGPointMake(newPoints.x - frame.size.width/2.0, newPoints.y - frame.size.height/2.0);
    lbl.frame = frame;
    [chartView addSubview:lbl];
}

chartView is a UIView adding on self.view in storyboard. Its size is (300.0, 300.0) and placed at center of the screen. Below is the output:

I tried to cover everything in my answer if still anything is unclear please feel free to comment. I'm also attaching my sample project for time saving.

Hope it helps!




回答2:


You don't need to draw an inner circle, just draw the slices using Bezier path.

https://developer.apple.com/documentation/uikit/uibezierpath https://developer.apple.com/library/content/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/BezierPaths/BezierPaths.html#//apple_ref/doc/uid/TP40010156-CH11-SW1

UIBezierPath *aPath = [UIBezierPath bezierPath];
  1. First draw line (rx+offset, ry+offset) to (rx+offset+10, ry+offset+10) [aPath addLineToPoint:]
  2. draw outer arc [aPath addarc:]
  3. draw line (rx+offset+10, ry+offset+10) to (rx+offset, ry+offset) [aPath addLineToPoint:]
  4. draw inner arc [aPath addarc:]
  5. closepath

or just set the line width of bezier path and draw bezier path with an arc that's it. [UIBezierPath bezierPathWithArcCenter:]

Then you can add text inside bezier path directly. Write text inside UIBezierPath




回答3:


Assuming you're trying to match the example, and assuming you're trying to align the text frames that read "Firefox", "IE7", "IE6", etc...

The centers of every text box around your pie chart all exist on another, invisible circle that surrounds your pie chart. You can imagine a line that points from the center, through the center point of each arc, to the theoretical circle upon which your text frames would sit.

Once you calculate the point on that invisible outer circle, you would then have a center point at which to put a UILabel (for simplicity sake).

If you're referring to the center label, and that isn't displaying, then you have a draw order problem, most likely. The white circle is drawing on top of the text, in such a case. In a CGGraphics context, everything is drawn in the painter method, meaning that everything you draw will draw "on top of" the previous work. In such a case, the text must be the last thing you draw (or, at least it must be drawn AFTER the white center circle)



来源:https://stackoverflow.com/questions/50168148/how-to-alligned-text-on-piechart-or-piechart-slice-center

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!