invalid context error when drawing a UIImage - ios

I am trying to draw a UIImage to the context of my UIView. I've used this code with the context stuff commented in and out ...
- (void)drawRect:(CGRect)rect
{
//UIGraphicsBeginImageContextWithOptions(rect.size,YES,[[UIScreen mainScreen] scale]);
//UIGraphicsBeginImageContext(rect.size);
UIImage *newImage = [UIImage imageNamed:#"character_1_0001.png"];
//[newImage drawAtPoint:CGPointMake(200, 200)];
[newImage drawInRect:rect];
//UIGraphicsEndImageContext();
}
As I understand it I shouldn't need to set the image context to do this and indeed without it I do see the image drawn in the view but I also get this error in the log ...
<Error>: CGContextSaveGState: invalid context 0x0
If I uncomment the UIGraphicsBeginImageContext lines I don't get anything drawn and no error.
Do I need to use the default graphics context and declare this somehow?
I need to draw the image because I want to add to it on the context and can't just generate a load of UIImageView objects.

It sounds like you are calling drawRect: directly. So it is getting called once from your call, and once from the genuine drawing loop.
With the context creation present, you do all the drawing in a new context then discard it, so you never see anything
With the context creation missing, you get the error from the manually called drawRect:, and the actual drawing takes place in the genuine drawRect: so you see your image.
Don't call drawRect: directly. Call setNeedsDisplay and drawRect: will be called for you at the appropriate point.

I discovered what was happening. The ViewController host of my custom UIView was instantiating a CALayer object. This was some old code that was previously being used as the drawing layer for a bitmap from an AVCaptureSession that I am using to generate my image.
I'm now not using the CALayer and am instead just drawing a slice of each frame of video onto the default graphics context in my view.
For some reason the instantiation of this CALayer was causing the conflict in the context.
Now it is not being used I only need to use the following code to draw the image ...
- (void)drawRect:(CGRect)rect
{
UIImage *newImage = [UIImage imageNamed:#"character_1_0001.png"];
[newImage drawInRect:rect];
}
Now I just do the same thing with an image that I pass in as a CGImageRef and adjust the rect ... hopefully.

Related

iOS: Adding an outline/stroke to an image context with a transparent background

The images that goes through here are PNGs of different shapes with a transparent background. In addition to merging them (which works fine), I'd like to give the new image a couple of pixels thick outline. But I can't seem to manage that.
(So just to clarify, I'm after an outline around the actual shapes in the context, not a rectangle around the entire image.)
+ (UIImage *)mergeBackgroundImage:(UIImage *)backgroundImage withOverlayingImage:(UIImage *)overlayImage{
UIGraphicsBeginImageContextWithOptions(backgroundImage.size, NO, backgroundImage.scale);
[backgroundImage drawInRect:CGRectMake(0, 0, backgroundImage.size.width, backgroundImage.size.height)];
[overlayImage drawInRect:CGRectMake(backgroundImage.size.width - overlayImage.size.width, backgroundImage.size.height - overlayImage.size.height, overlayImage.size.width, overlayImage.size.height)];
//Add stroke.
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
Thanks for your time!
Markus
If you make a CALayer who's backing is set to a CGImage of your image, you can then use it as a masking layer for your layer that requires an outline1. And once you've done that, you can render your layer into another context, and then get another UIImage from that.
// edit: Something like what's describe in this answer.

iOS7's drawViewHierarchyInRect doesn't work?

From what I've read, iOS7's new drawViewHierarchyInRect is supposed to be faster than CALayer's renderInContext. And according to this and this, it should be a simple matter of calling:
[myView drawViewHierarchyInRect:myView.frame afterScreenUpdates:YES];
instead of
[myView.layer renderInContext:UIGraphicsGetCurrentContext()];
However, when I try this, I just get blank images. Full code that does the capture, where "self" is a subclass of UIView,
// YES = opaque. Ignores alpha channel, so less memory is used.
// This method for some reasons renders the
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale); // Still slow.
if ( [AIMAppDelegate isOniOS7OrNewer] )
[self drawViewHierarchyInRect:self.frame afterScreenUpdates:YES]; // Doesn't work!
else
[self.layer renderInContext:UIGraphicsGetCurrentContext()]; // Works!
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
contentImageView.image = image; // this is empty if done using iOS7's way
and contentImageView is a UIImageView that is added as a subView to self during initialization.
Additionally, the drawing that I want captured in the image is contained in other sub-views that are also added to self as a sub-view during initialization (including contentImageView).
Any ideas why this is failing when using drawViewHierarchyInRect?
* Update *
I get an image if I draw a specific sub-view, such as:
[contentImageView drawViewHierarchyInRect:contentImageView.frame afterScreenUpdates:YES];
or
[self.curvesView drawViewHierarchyInRect:self.curvesView.frame afterScreenUpdates:YES];
however I need all the visible sub-views combined into one image.
Try it with self.bounds rather than self.frame—it’s possible you’re getting an image of your view rendered outside the boundaries of the image context you’ve created.

Taking a screenshot of a view that is currently not on screen on iOS

I'm trying to make a transition between two ViewControllers. My Transition class has a property for the destination view controller. When I try to get a screenshot of the destination's view, I use this method:
+ (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame {
// Create a new context the size of the frame
UIGraphicsBeginImageContextWithOptions(frame.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// Render the view
[view.layer renderInContext:context];
// Get the image from the context
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
// Cleanup the context you created
UIGraphicsEndImageContext();
return renderedImage;
}
So when I want the image, I'll do this:
UIImage *image = [UIImage renderImageFromView:destinationController.view withRect:destinationController.view.bounds];
I tried it on a blank UIViewController with an orange background color and a label. I get the orange background color, but I do not get the label that was created in IB. Is there a way to get a proper screenshot of the new view I plan on showing? Thanks!
You need to make sure the view's layer is drawn to first. Calling renderInContext on a CALayer will only recursively call additional child CALayers. You need your child UIViews to draw themselves, not just using the CALayer.
Try calling
[view drawrect:frame];
instead of
[view.layer renderInContext:context];
As long as you have an open graphics context(which you do at that point), drawrect should draw directly into that.

renderInContext flips the origin of colorWithPatternImage

I have UIView with backgroundColor set with colorWithPatternImage. As expected, the background image is drawn starting at the top left corner.
Problem appears when I'm doing renderInContext on that view: the background image is drawn starting at the bottom left corner. Everything else seems to render fine.
Here's the source and destination images:
Here is the code:
// here is the layer to be rendered into an image
UIView *src = [[UIView alloc] initWithFrame:(CGRect){{0, 0}, {100, 100}}];
src.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
[self.view addSubview:src];
// here we'll display the image
UIImageView *dest = [[UIImageView alloc] initWithFrame:(CGRect){{110, 0}, src.bounds.size}];
[self.view addSubview:dest];
// render `src` to an image in `dest`
UIGraphicsBeginImageContext(src.bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[src.layer renderInContext:context];
dest.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Is there a way to keep the image to tile in right direction, as in the src view?
I managed to work around this by calling CGContextSetPatternPhase with a height of modf(viewHeight, patternHeight)
https://developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_overview/dq_overview.html#//apple_ref/doc/uid/TP30001066-CH202-CJBBAEEC
This doc explains why this happens, in particular this part:
Important: The above discussion is essential to understand if you plan to write applications that directly target Quartz on iOS, but it is not sufficient. On iOS 3.2 and later, when UIKit creates a drawing context for your application, it also makes additional changes to the context to match the default UIKIt conventions. In particular, patterns and shadows, which are not affected by the CTM, are adjusted separately so that their conventions match UIKit’s coordinate system. In this case, there is no equivalent mechanism to the CTM that your application can use to change a context created by Quartz to match the behavior for a context provided by UIKit; your application must recognize the what kind of context it is drawing into and adjust its behavior to match the expectations of the context.

Iphone sdk save drawings without whole context

I want to capture screen of my view from my iPhone app. I have white background view and on that I draw a lines on that view's layer using this method .
- (void)draw {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
if (self.fillColor) {
[self.fillColor setFill];
[self.path fill];
}
if (self.strokeColor) {
[self.strokeColor setStroke];
[self.path stroke];
}
CGContextRestoreGState(context);
}
- (void)drawRect:(CGRect)rect {
// Drawing code.
for (<Drawable> d in drawables) {
[d draw];
}
[delegate drawTemporary];
}
I have use delegate methods to draw lines on layer.
This is the project link from where I get help for this.
https://github.com/search?q=dudel&type=Everything&repo=&langOverride=&start_value=1
Now when I use the following context methods to save the drawing only I successfully save it without that white background.
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
But When I use the following method of Bezier Pathe I cannot save the drawing without its white background,It saves the whole screen i.e. that drawing and its background.
UIGraphicsBeginImageContext(self.view.bounds.size);
[dudelView.layer renderInContext:UIGraphicsGetCurrentContext()];
//UIImage *finishedPic = UIGraphicsGetImageFromCurrentImageContext();
So can anybody help me how can I save the drawing only here in this app.
(You've tagged this as MapKit related, but make no mention of MapKit.)
Why don't you just split your drawing sequence into three chunks?
Draw your paths into an image context and get a UIImage, as you described.
Draw a background color.
Draw the UIImage.
Then you can use the UIImage for your "screenshot" as well.
I should also note that if the only thing you don't want in your captured UIImage is the background color, you are better off creating a UIImageView, setting its background color (-setBackgroundColor:), and setting its image to be your UIImage.
UIImageView internally has a number of optimizations that allow it to display graphics with much higher performance than you can get with a custom -drawRect: implementation.
Why don't you just save the drawables array? Those are all the drawings without the underlying image :)

Resources