views:

355

answers:

2

I have a CALayer that implements drawInContext: and draws a simple circle, like so:

- (void)drawInContext:(CGContextRef)ctx
{
     CGContextScaleCTM(ctx, DrawingScale, DrawingScale);
     CGContextSetRGBFillColor (ctx, 1, 0, 0, 1);
     CGContextFillEllipseInRect (ctx, LocationRectangle);
}

I have been experimenting with different ways of increasing the size of my circle based on a touch event. I've tried using the UIView beginAnimation system and CABasicAnimation's, bbut they all blur the image. In my reading I have discovered that is because the CALayer's seem to be treated like bitmaps for these options.

Fair enough... but I want to scale my graphics without blurring, e.g. to use vector scaling.

As such, in my code I have a "DrawingScale" property.

Is there a neat way to scale this property using Layer animations? Or is this something people do with NSTimers (yuk)?

Might this be one situation where I can use Core Animation's ability to animate custom properties? If so, I'd love to know which example people find is the best, or which section of the Apple docs might be a good place to start with this topic.

It seems when it comes to graphics on Mac/iOS, there are many ways to skin/draw a cat...?

+1  A: 

Use a shape layer (CAShapeLayer). You pass it a path, fill color, and stroke color and it will keep all of your edges crisp no matter the scale. If all you need is a circle though, you could just cheat and create a layer whose corner radius is half the width and height (assuming perfect square/circle). Something like:

CALayer *layer = [CALayer layer];
[layer setMasksToBounds:YES];
[layer setBackgroundColor[[UIColor redColor] CGColor]];
[layer setCornerRadius:25.0f];
[layer setBounds:CGRectMake(0.0f, 0.0f, 50.0f, 50.0f)];
[layer setPosition:CGPointMake([view bounds].size.width/2.0f, 
                               [view bounds].size.height/2.0f]);

[[view layer] addSublayer:layer];

You might also want to take a look at the latest WWDC videos from Apple. There are three sessions in there on Core Animation. The discuss layer scaling and keeping the edges crisp. The videos are free.

Best regards.

Matt Long
Thanks for the answer Matt. At the moment it's just a circle, but it will be more. But I loved the creative solution :-) Nifty little cheat. I will have a play with CALayer!Funny you mention the WWDC videos, I just saw the email from Apple about those and downloaded several Gb worth. Now I just have to find the many hours of time to watch them all :-)I will post the solution I have been using in the mean time also
Fuzz
A: 

After spending a few days working on this, one solution I have is to have a variable called "DrawingScale" as a member variable (is that what they are called in Obj-C) and using Core Animation to animate that property.

- (void)drawInContext:(CGContextRef)ctx
{
    CGFloat size = (CGFloat)(DrawingScale*DEFAULT_SIZE);
    CGRect elipseRect = CGRectMake(xPos, yPos, size, size);
    CGContextStrokeEllipseInRect(ctx, elipseRect);
    CGContextFillEllipseInRect (ctx, elipseRect);
}

Then on an event, I set up an animation as such:

-(void) : (CGPoint) location
{
    /* Set up an explicit animation to scale the players size up */
    CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"DrawingScale"];
    anim.fromValue = [NSNumber numberWithFloat: 1];
    anim.toValue = [NSNumber numberWithFloat: 5];
    anim.removedOnCompletion = YES;
    anim.duration = 0.1;
    anim.delegate = self;
    anim.fillMode = kCAFillModeForwards;
    anim.autoreverses = NO;
    [self addAnimation: anim forKey:@"ElipseGettingBigger"];
}

This sets up the animation to set my variable DrawingScale to range from 1 to 5, and do so within 0.1 of a second. It works really well.

I did need to be careful and make sure I had adjusted the layer bounds accordingly first too!

Fuzz