imObjCSwifting
imObjCSwifting

Reputation: 743

iOS draw simple bar charts like Mint app

I want to create a custom cell like one in the Mint app which does pretty much the same thing. How do I go about drawing the two bars using the earned and spent data?

Thanks,

custom Cell

I got this far:

-(void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    UIColor * earningStartColor = [UIColor colorWithRed:15/255.0f green:227/255.0f blue:0/255.0f alpha:1.0f];
    UIColor * earningEndColor = [UIColor colorWithRed:15/255.0f green:188/255.0f blue:0/255.0f alpha:1.0f];

    CGRect earningRect = CGRectMake(5, 32, 60, 13);

    UIBezierPath *pathE = [UIBezierPath bezierPathWithRoundedRect:earningRect
                                                     cornerRadius:3.0];
    [pathE addClip];

    drawGlossAndGradient(context, earningRect, earningStartColor.CGColor, earningEndColor.CGColor);


    UIColor * spentStartColor = [UIColor colorWithRed:255/255.0f green:88/255.0f blue:67/255.0f alpha:1.0f];
    UIColor * spentEndColor = [UIColor colorWithRed:255/255.0f green:52/255.0f blue:49/255.0f alpha:1.0f];

    CGRect spentRect = CGRectMake(5, 52, 25, 13);    

    UIBezierPath *pathS = [UIBezierPath bezierPathWithRoundedRect:spentRect
                                                    cornerRadius:5.0];
    [pathS addClip];

    drawGlossAndGradient(context, spentRect, spentStartColor.CGColor, spentEndColor.CGColor);
}

However, after addClip the drawing stops so only one bar is displayed. If i wrapped addClip around CGContextSaveGState & CGContextRestoreGState, only the second bar corners are rounded.

I also tried subclass a view and draw on the view then add as a subview to my tableviewcell and use cornerRadius, but the drawing is actually lying behind the view, so it appears as the rounded view (with its background) with the rectangle bar behind. I thought this should be easier than it is.

Upvotes: 0

Views: 1441

Answers (3)

Guo Luchuan
Guo Luchuan

Reputation: 4731

Your bug is because of the [pathE addClip];

About the addClip method , the apple doc said :

Important: If you need to remove the clipping region to perform subsequent drawing operations, you must save the current graphics state (using the CGContextSaveGState function) before calling this method. When you no longer need the clipping region, you can then restore the previous drawing properties and clipping region using the CGContextRestoreGState function.

so you should save the graphics state before addClip and restore the graphics state after addClip

This can help you :

-(void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    UIColor * earningStartColor = [UIColor colorWithRed:15/255.0f green:227/255.0f blue:0/255.0f alpha:1.0f];
    UIColor * earningEndColor = [UIColor colorWithRed:15/255.0f green:188/255.0f blue:0/255.0f alpha:1.0f];

    CGRect earningRect = CGRectMake(5, 32, 60, 13);

    UIBezierPath *pathE = [UIBezierPath bezierPathWithRoundedRect:earningRect
                                                     cornerRadius:3.0];
    CGContextSaveGState(context);
    [pathE addClip];

    drawGlossAndGradient(context, earningRect, earningStartColor.CGColor, earningEndColor.CGColor);
    CGContextRestoreGState(context);

    UIColor * spentStartColor = [UIColor colorWithRed:255/255.0f green:88/255.0f blue:67/255.0f alpha:1.0f];
    UIColor * spentEndColor = [UIColor colorWithRed:255/255.0f green:52/255.0f blue:49/255.0f alpha:1.0f];

    CGRect spentRect = CGRectMake(5, 52, 25, 13);

    UIBezierPath *pathS = [UIBezierPath bezierPathWithRoundedRect:spentRect
                                                     cornerRadius:5.0];
    [pathS addClip];

    drawGlossAndGradient(context, spentRect, spentStartColor.CGColor, spentEndColor.CGColor);
}

PS: I think you can post your drawGlossAndGradient to the stackoverflow , so others can help easy , and may be helpful for others who search the answer.

And I fake the drawGlossAndGradient method

void drawGlossAndGradient(CGContextRef context, CGRect rect, CGColorRef startColor,
                          CGColorRef endColor) {
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat locations[] = {0.0, 1.0};

    NSArray *colors = [NSArray arrayWithObjects:(__bridge id) startColor, (__bridge id) endColor, nil];

    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
                                                        (__bridge CFArrayRef) colors, locations);

    CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));

    CGContextAddRect(context, rect);
    CGContextClip(context);
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);

    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);

}

The effect like this :

enter image description here

Upvotes: 3

fumoboy007
fumoboy007

Reputation: 5553

If you want to save yourself some coding, take a look at Core Plot, a Mac/iOS framework for drawing and animating graphs.

Upvotes: 1

Rui Peres
Rui Peres

Reputation: 25927

That's not more than a UIView with a gradient and round corners. This is more than enough for you.

Round corners:

#import <QuartzCore/QuartzCore.h>
...

view.layer.cornerRadius = 5;
view.layer.masksToBounds = YES;

Taken from here.

Upvotes: 2

Related Questions