Ricardo Sanchez-Saez
Ricardo Sanchez-Saez

Reputation: 9566

Draw CGContext twice within the same UIView

I have a UIView subclass with a custom drawRect: method which does some expensive calculations to build a path and draws it using CGContextStrokePath() on the current context.

If my view has a size (x, y), I want to do my custom drawing on (0, 0), (x/2, y) and then fill the (x/2, 0), (x, y) area with a mirrored image of the first half.

What is the simplest/fastest way of doing this, other than manually duplicating the points to be drawn? Maybe rendering the half view to a bitmap image context and using that one to render on both halves of the current image context? The content is animated, so drawRect: is roughly executed 60 times per second.

Upvotes: 1

Views: 903

Answers (2)

Ricardo Sanchez-Saez
Ricardo Sanchez-Saez

Reputation: 9566

One efficient way of doing this is to do the draw operations on the context of a CGLayer, and then use the layer to draw on the current UIView's context.

See Core Graphics Layer Drawing on the Quartz 2D Programming Guide for more details.

Basically you would do this:

- (void)drawRect:(CGRect)rect
{
    const CGFloat viewWidth = view.bounds.size.width;
    const CGFloat viewHeight = view.bounds.size.height;
    CGContextRef viewContext = UIGraphicsGetCurrentContext();
    CGLayerRef layer = CGLayerCreateWithContext(viewContext, CGSizeMake(viewWidth/2, viewHeight), 0);
    CGContextRef layerContext = CGLayerGetContext(layer);
    // Draw to the layerContext
    // using CGContextStrokePath() or whatever
    CGContextDrawLayerAtPoint(viewContext, CGPointZero, layer);
    CGContextTranslateCTM(viewContext, viewWidth, 0);
    CGContextScaleCTM(viewContext, -1, 1);
    CGContextDrawLayerAtPoint(viewContext, CGPointZero, layer);
    CGLayerRelease(layer);
}

Additionally, you can store the CGLayerRef as an ivar of UIView so you don't recreate it every frame.


This solution works great in my case, but two caveats:

  • It seems Apple no longer recommends using CGLayers, as stated here.
  • In order to properly render to retina screens using CGLayers you have to follow the steps detailed here

Upvotes: 2

James Snook
James Snook

Reputation: 4093

You could calculate the CGPath once, you can either do this by building the whole Path as a CGPath instead of building it as part of the CGContext, or you could use the CGContextCopyPath to get the CGPath after creating it in the context. Then you could stroke the path once. Then put the CGPath back into the CGContext using CGContextAddPath and change the change the transform on the CGContext to get your mirror with a transform something like this (-1, 0, 0, 1, width, 0), the -1 does the reflection about the x-axis, and the width should move it back to the the correct position, and stroke the path again.

Upvotes: 0

Related Questions