My issue is as follows – I’m able to use the below function: takeSnapshotOfScreen to take a properly scaled snapshot of the screen when there exists only an image in any UIView. However, when I add a label, as seen below, the final snapshot image saves down with a smaller, squished version of the larger image, superimposed in the center where the CustomLabelClass should be, instead of the label containing actual text.
My guess is that the call to get the CG context, from within the function: drawTextInRect (in CustomLabelClass), seems to be reusing the context retrieved by the calling function takeSnapshotOfScreen, since it is never popped, I’m guessing.
If my guess is correct, how does one properly handle such a scenario?
My sincere thanks in advance to any assistance. I really appreciate it!
- Code & Details-
Below is a sample image, demonstrating this superimposed/context problem.

I have a custom UILabel class where I’m overriding drawTextInRect to draw a border around the text:
@interface CustomLabelClass : UILabel {
@property (nonatomic, strong) UIColor *innerColor;
@property (nonatomic, strong) UIColor *borderColor;
}
@implementation CustomLabelClass
@synthesize innerColor, borderColor;
...
- (void)drawTextInRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetTextDrawingMode(context, kCGTextFill);
if(innerColor)
[self setTextColor:innerColor];
// Draw the text without an outline
[super drawTextInRect:rect];
CGImageRef alphaMask = NULL;
// Create a mask from the text
alphaMask = CGBitmapContextCreateImage(context);
// Outline width
CGContextSetLineWidth(context, 4);
CGContextSetLineJoin(context, kCGLineJoinRound);
// Set the drawing method to stroke
CGContextSetTextDrawingMode(context, kCGTextStroke);
// Outline color
if(borderColor)
self.textColor = borderColor;
// notice the +1 for the y-coordinate. this is to account for the face that the outline appears to be thicker on top
[super drawTextInRect:CGRectMake(rect.origin.x, rect.origin.y+1, rect.size.width, rect.size.height)];
// Draw the saved image over the outline. Invert because CoreGraphics works with an inverted coordinate system
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, rect, alphaMask);
// Clean up because ARC doesnt handle CoreGraphics
CGImageRelease(alphaMask);
UIGraphicsEndImageContext();
}
I have another class that uses the CustomLabelClass, below:
#import "CustomLabelClass.h"
@implementation CustomViewController
-(void)createCustomLabel{
//...create a CustomLabelClass *newLabel object...etc.
...
// add it to the view
[[[self view] viewWithTag:ViewContainingImageAndCustomLabels] addSubview:newLabel];
}
-(void)updateCurrentImage{
// Assume that the object: mainImage, is a global UIImage, for purposes of these code snippets.
UIImageView *mainImageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, mainImage.size.width, mainImage.size.height)];
mainImageView.image = mainImage;
[mainImageView setTag:ViewTypeMainPhoto];
// we always want the image to be the underlying view, so insert it at Index:0
[[[self view] viewWithTag:ViewContainingImageAndCustomLabels] insertSubview:mainImageView atIndex:0];
}
-(UIImage *)takeSnapshotOfScreen{
// Mostly taken from: http://developer.apple.com/library/ios/#qa/qa1703/_index.html
//
// Create a graphics context with the target size
// On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
// On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
CGSize imageSize = [[UIScreen mainScreen] bounds].size;
if (NULL != UIGraphicsBeginImageContextWithOptions){
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0.0f); // 0.0f scale is for retina bc *WithOptions captures native resolution
}else{
UIGraphicsBeginImageContext(imageSize);
}
CGContextRef context = UIGraphicsGetCurrentContext();
// Iterate over every window from back to front
for (UIView *tempView in [[[self view] viewWithTag:ViewContainingImageAndCustomLabels] subviews])
{
// -renderInContext: renders in the coordinate space of the layer,
// so we must first apply the layer's geometry to the graphics context
CGContextSaveGState(context);
// Center the context around the window's anchor point
CGContextTranslateCTM(context, [tempView center].x, [tempView center].y);
// Apply the window's transform about the anchor point
CGContextConcatCTM(context, [tempView transform]);
// Offset by the portion of the bounds left of and above the anchor point
CGContextTranslateCTM(context,
-[tempView bounds].size.width * [[tempView layer] anchorPoint].x,
-[tempView bounds].size.height * [[tempView layer] anchorPoint].y);
// Render the layer hierarchy to the current context
[[tempView layer] renderInContext:context];
// Restore the context
CGContextRestoreGState(context);
}
// Retrieve the screenshot image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext(); // remove context from stack
return image;
}
and finally a function that just calls the above (simplified below):
-(void)doEverything{
[self createCustomLabel];
[self updateCurrentImage];
[self takeSnapshotOfScreen];
}
@end
The solution was to set
setNeedsDisplayon the subview containing the manually rendered text because I’m overridingdrawTextInRect.