ios - 如何对齐饼图(或)饼图切片中心上的文本?

标签 ios objective-c pie-chart

enter image description here

我想在饼图中添加文本。

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

从上面我可以用内部空白绘制饼图,但我无法在特定饼图切片中添加文本。任何人都可以提供一些好的例子吗?

我的饼图显示完美,但文本对齐方式不正确。

//对于文本我会试试这个代码

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

最佳答案

尽管您的问题很不清楚,您希望如何将 label 准确地居中到 pie slice。我尝试了我从上面发布的问题中理解的内容。

首先是使用 UIBezierPath 而不是 CGContext。我用了 Parametric equaction of circle计算我的圆的边界点。

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

它将为您提供需要显示标签的点。现在您只需计算正确的角度(切片中心)即可实现您的目标。

UIBezierPath 有一个方法 [bezierPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; 创建一个圆弧。

下面我一步一步来分享代码:

两个宏:

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

viewDidLoad: 为我的图表初始化虚拟数据的方法。

- (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:方法

- (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;
    }
}

制作饼图切片方法

- (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 是在 storyboard 的 self.view 上添加的 UIView。它的大小为 (300.0, 300.0) 并位于屏幕中央。以下是输出:

enter image description here

我尽量在回答中涵盖所有内容,如果仍有任何不清楚的地方,请随时发表评论。我还附上了我的 sample project节省时间。

希望对您有所帮助!

关于ios - 如何对齐饼图(或)饼图切片中心上的文本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50168148/

相关文章:

objective-c - 使用 execl 从应用程序内部编译文件

objective-c - 标签栏项目的标题不显示

javascript - 如何向JQWidget饼图添加数组?

iphone - 在 iPhone 上运行我的应用程序时图像模糊

ios - 是否可以只从 scheduledLocalNotifications 数组中的对象获取时间?

ios - Xamarin iOS - 绑定(bind)项目 - 警告

objective-c - View 应仅显示为纵向模式

javascript - 如何使用chart.js向饼图中的图例字段添加百分比值

python - Matplotlib:在堆叠饼图中放大、填充图例并将其居中

objective-c - 在访问Objective C属性时可以具有参数吗?