简体   繁体   中英

Trying to implement offset radius on piechart using QuartzCore

I'm using the XYPieChart library to draw my pie chart. I don't need slice selection so I manually disabled that and also I will always have 3 slices.

This is how my chart looks now:

在此处输入图片说明

It is working fine, but I want it to look like this:

在此处输入图片说明

This is my current code where most things occur:

- (void)reloadData
{
    if (_dataSource)
    {
        CALayer *parentLayer = [_pieView layer];
        NSArray *slicelayers = [parentLayer sublayers];

        double startToAngle = 0.0;
        double endToAngle = startToAngle;

        NSUInteger sliceCount = [_dataSource numberOfSlicesInPieChart:self];

        double sum = 0.0;
        double values[sliceCount];
        for (int index = 0; index < sliceCount; index++) {
            values[index] = [_dataSource pieChart:self valueForSliceAtIndex:index];
            sum += values[index];
        }

        NSLog(@"%f", values[0]);
        NSLog(@"%f", values[1]);
        NSLog(@"%f", values[2]);


        double angles[sliceCount];
        for (int index = 0; index < sliceCount; index++) {
            double div;
            if (sum == 0)
                div = 0;
            else
                div = values[index] / sum ;
            angles[index] = M_PI * 2 * div;

            NSLog(@"sum %f", sum);
            NSLog(@"pi %f", M_PI);
        }
        NSLog(@"%f", angles[0]);
        NSLog(@"%f", angles[1]);
        NSLog(@"%f", angles[2]);


        [CATransaction begin];
        [CATransaction setAnimationDuration:_animationSpeed];

        [_pieView setUserInteractionEnabled:NO];

        __block NSMutableArray *layersToRemove = nil;

        BOOL isOnStart = ([slicelayers count] == 0 && sliceCount);
        NSInteger diff = sliceCount - [slicelayers count];
        layersToRemove = [NSMutableArray arrayWithArray:slicelayers];

        BOOL isOnEnd = ([slicelayers count] && (sliceCount == 0 || sum <= 0));
        if(isOnEnd)
        {
            for(SliceLayer *layer in _pieView.layer.sublayers){
                [layer createArcAnimationForKey:@"startAngle"
                                      fromValue:[NSNumber numberWithDouble:_startPieAngle]
                                        toValue:[NSNumber numberWithDouble:_startPieAngle] 
                                       Delegate:self];
                [layer createArcAnimationForKey:@"endAngle" 
                                      fromValue:[NSNumber numberWithDouble:_startPieAngle]
                                        toValue:[NSNumber numberWithDouble:_startPieAngle] 
                                       Delegate:self];
            }
            [CATransaction commit];
            return;
        }

        for(int index = 0; index < sliceCount; index ++)
        {
            SliceLayer *layer;
            double angle = angles[index];
            endToAngle += angle;
            double startFromAngle = _startPieAngle + startToAngle;
            double endFromAngle = _startPieAngle + endToAngle;

            NSLog(@"slice layers %d", slicelayers.count);


            layer = [self createSliceLayer];
            if (isOnStart)
                startFromAngle = endFromAngle = _startPieAngle;
            [parentLayer addSublayer:layer];
            diff--;


            if( index >= [slicelayers count] )
            {
                layer = [self createSliceLayer];
                if (isOnStart)
                    startFromAngle = endFromAngle = _startPieAngle;
                [parentLayer addSublayer:layer];
                diff--;
            }
            else
            {
                SliceLayer *onelayer = [slicelayers objectAtIndex:index];
                if(diff == 0 || onelayer.value == (CGFloat)values[index])
                {
                    layer = onelayer;
                    [layersToRemove removeObject:layer];
                }
                else if(diff > 0)
                {
                    layer = [self createSliceLayer];
                    [parentLayer insertSublayer:layer atIndex:index];
                    diff--;
                }
                else if(diff < 0)
                {
                    while(diff < 0) 
                    {
                        [onelayer removeFromSuperlayer];
                        [parentLayer addSublayer:onelayer];
                        diff++;
                        onelayer = [slicelayers objectAtIndex:index];
                        if(onelayer.value == (CGFloat)values[index] || diff == 0)
                        {
                            layer = onelayer;
                            [layersToRemove removeObject:layer];
                            break;
                        }
                    }
                }
            }

            layer.value = values[index];
            layer.percentage = (sum)?layer.value/sum:0;
            UIColor *color = nil;
            if([_dataSource respondsToSelector:@selector(pieChart:colorForSliceAtIndex:)])
            {
                color = [_dataSource pieChart:self colorForSliceAtIndex:index];
            }

            if(!color)
            {
                color = [UIColor colorWithHue:((index/8)%20)/20.0+0.02 saturation:(index%8+3)/10.0 brightness:91/100.0 alpha:1];
            }

            [layer setFillColor:color.CGColor];

            [layer createArcAnimationForKey:@"startAngle"
                                  fromValue:[NSNumber numberWithDouble:startFromAngle]
                                    toValue:[NSNumber numberWithDouble:startToAngle+_startPieAngle] 
                                   Delegate:self];
            [layer createArcAnimationForKey:@"endAngle" 
                                  fromValue:[NSNumber numberWithDouble:endFromAngle]
                                    toValue:[NSNumber numberWithDouble:endToAngle+_startPieAngle] 
                                   Delegate:self];
            startToAngle = endToAngle;
        }
        [CATransaction setDisableActions:YES];
        for(SliceLayer *layer in layersToRemove)
        {
            [layer setFillColor:[self backgroundColor].CGColor];
            [layer setDelegate:nil];
            [layer setZPosition:0];
            CATextLayer *textLayer = [[layer sublayers] objectAtIndex:0];
            [textLayer setHidden:YES];
        }

        [layersToRemove enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            [obj removeFromSuperlayer];
        }];

        [layersToRemove removeAllObjects];
        for(SliceLayer *layer in _pieView.layer.sublayers)
        {
            [layer setZPosition:kDefaultSliceZOrder];

            CGPoint currPos = layer.position;
            double middleAngle = (layer.startAngle + layer.endAngle)/2;
            CGPoint newPos = CGPointMake(currPos.x, currPos.y);
            layer.position = newPos;


            NSLog(@"seno %f coseno %f", _selectedSliceOffsetRadius*cos(middleAngle), _selectedSliceOffsetRadius*sin(middleAngle));

            NSLog(@"value: %f", layer.value);
            NSLog(@"asa %f %f middle: %f",layer.startAngle,layer.endAngle, middleAngle);
            NSLog(@"position x: %f", layer.position.x);
            NSLog(@"postion y: %f", layer.position.y);
        }

        [_pieView setUserInteractionEnabled:NO];

        [CATransaction setDisableActions:NO];
        [CATransaction commit];
    }
}

Now, when I change the following line:

CGPoint newPos = CGPointMake(currPos.x, currPos.y);

to:

CGPoint newPos = CGPointMake(currPos.x + _selectedSliceOffsetRadius*cos(middleAngle), currPos.y + _selectedSliceOffsetRadius*sin(middleAngle));

I get the expected result if the slices are equal, if they are not equal, I get images similar to this: 在此处输入图片说明

By the way, _selectedSliceOffsetRadius = 10; . I've been working on this for the past day, got close, but still it isn't ready.

Any suggestions will be greatly appreciated.

UPDATE:

After the great answer below, I got to the following:

在此处输入图片说明

在此处输入图片说明

There's still a little thin line on the borders. I'm not sure why I'm getting this effect.

UPDATE2:

Just fixed adding one to the line:

float radiusOfPieChart = self.pieChartRight.frame.size.width/2+1;

I guess it had something to do with the margins of the chart, so increasing the radius a little bit makes the problem go away.

I used the example project XYPieChart comes with to create this. I have never used the XYPieChart library/framework before, so the code below might be a little bit abundant, but it should work.

// Some variables we're going to use
CGFloat totalValueOfSlices;
CGFloat offset = -90.0f /*YOUR_CHART_OFFSET (default should be -90)*/;
CGFloat percentage = 0;
CGPoint originOfPieChart = CGPointMake(self.pieChart.frame.size.width/2, self.pieChart.frame.size.height/2);
CGFloat radiusOfPieChart = self.pieChart.frame.size.width/2;
UIBezierPath *path = [UIBezierPath bezierPath];

// The first thing we want to know is what our total value of the piechart is
for(NSNumber *number in self.slices)
{
    totalValueOfSlices += [number floatValue];
}

// Now for each slice, we're going to draw a line
for (NSNumber *number in _slices)
{
    // move point to the origin of the pie chart
    [path moveToPoint:originOfPieChart];

    // Get the value of the current slice
    CGFloat sliceValueAsFloat = [number floatValue];

    // Calculate how much percentage this is based on the totalValueOfSlices and the previous percentages
    percentage += sliceValueAsFloat/totalValueOfSlices;

    // We multiply the percentage by 360, so we get the degrees
    CGFloat degrees = percentage * 360 + offset;

    // Now we turn degrees to radians, we use them in the next formula
    CGFloat radians = degrees * 0.0174532925;

    // Now Based on the radians, radius and origin, we're going to calculate where to draw to
    CGFloat x = originOfPieChart.x + (radiusOfPieChart * cos(radians));
    CGFloat y = originOfPieChart.y + (radiusOfPieChart * sin(radians));

    // Time to draw our line
    [path addLineToPoint:CGPointMake(x, y)];

}
// Now we're going to add a sublayer to the piechart, that adds our drawn lines
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [path CGPath];
shapeLayer.strokeColor = [[UIColor blueColor] CGColor]; /*YOUR_BACKGROUND_COLOR*/;
shapeLayer.lineWidth = 16.0;
shapeLayer.fillColor = [[UIColor clearColor] CGColor];
[self.pieChart.layer addSublayer:shapeLayer];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM