iphone sdk: UIScrollView as Image - iphone

I want to save the content of an UiScrollView as an Image. It seems that it is not as easy as the UIImageRepresentation that can be used for Views or ImageViews, anyone an idea how to do it?

I usually use a method implemented in a category:
+ (UIImage *)screenshot:(UIView *)view {
UIGraphicsBeginImageContext(view.bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CALayer *layer = view.layer;
[layer renderInContext:context];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
but, in your case, I suppose it will not suit well if your need is to get the whole content (also the part not visible in that moment). You could change the zoom value and then take the screenshot if it's a good enough solution.

Related

How do I take a screenshot of a view which has transform?

I'm able to crop a view with this code
- (UIImage *)captureScreenInRect:(CGRect)captureFrame {
CALayer *layer;
layer = self.view.layer;
UIGraphicsBeginImageContext(self.view.bounds.size);
CGContextClipToRect (UIGraphicsGetCurrentContext(),captureFrame);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenImage;
}
But I have an imageview zoomed in with transform and it isn't shown to scale.
How do I capture EXACTLY what the user sees on the screen
The Stack Overflow question "renderInContext:" and CATransform3D has more info, but the gist is:
QCCompositionLayer, CAOpenGLLayer, and QTMovieLayer layers are not rendered. Additionally, layers that use 3D transforms are not rendered, nor are layers that specify backgroundFilters, filters, compositingFilter, or a mask values.
(from the CALayer docs).
More info is also available in this technical Q&A: http://developer.apple.com/library/ios/#qa/qa1703/_index.html
If your app is not going to the app store you can use the undocumented UIGetScreenImage API:
// Define at top of implementation file
CGImageRef UIGetScreenImage(void);
...
- (void)buttonPressed:(UIButton *)button
{
// Capture screen here...
CGImageRef screen = UIGetScreenImage();
UIImage* image = [UIImage imageWithCGImage:screen];
CGImageRelease(screen);
// Save the captured image to photo album
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
}
(from John Muchow)
However, use of this API will make your app not get approved.
I have been unable to find any other workarounds.

Non-null invalid context error when creating a UIImage from a UIView

I'm using the following to generate a UIImage from a UIView
UIGraphicsBeginImageContextWithOptions(view.frame.size, YES, [[UIScreen mainScreen] scale]);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
which usually works but about half the time, I get this error when renderInContext is called:
<Error>: CGContextDrawImage: invalid context 0x84d0b20
Does anyone have any idea why this happens or how to even detect when it happens? I think I would feel better if the address for the context was 0x0 because it would at least be something I could test for and deal with but this so far this has me stumped.
Edit: Whoops. Meant to use view.frame.size and not view.bounds.size.
If you create a context with width or height zero, the context will be invalid. This could happen if the view size is zero on either axis, or if the view is nil.
Also, since you're rendering a layer, you might want to get the layer bounds and scale, instead of the view bounds and screen scale. Most of the time these values will be the same, but it's good to allow for the times they are not.
// Debugging output to see how if an invalid context is being created due to zero size.
NSLog(#"View = %#", view);
NSLog(#"View size = %#", NSStringFromCGSize(view.bounds.size));
UIGraphicsBeginImageContextWithOptions(view.layer.bounds.size, YES, view.layer.contentsScale);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I had a similar issue but was able to solve it by pushing and popping the image context prior to my layer rendering its contents.
Try something like this:
UIImage *snapshotImage = nil;
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, view.layer.contentsScale);
{
CGContextRef imageContext = UIGraphicsGetCurrentContext();
if (imageContext != NULL) {
UIGraphicsPushContext(imageContext);
{
[view.layer renderInContext:imageContext];
}
UIGraphicsPopContext();
}
snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
For what it's worth, there are alternative (and more efficient) ways to obtain a snapshot but it's still under NDA.
UIGraphicsBeginImageContext(self.view.frame.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Hope, this will help you....

Add 2 UIImages into One UIImage

I am adding 2 images to each other and wanted to know if this is a good way to do this? This code works and looked to be powerful.
So, my question really is, It this good or is there a better way?
PS: Warning code written by a designer.
Call the function:
- (IBAction) {
UIImage *MyFirstImage = UIImage imageNamed: #"Image.png"];
UIImage *MyTopImage = UIImage imageNamed: #"Image2.png"];
CGFloat yFloat = 50;
CGFloat xFloat = 50;
UIImage *newImage = [self placeImageOnImage:MyFirstImage imageOver:MyTopImage x:&xFloat y:&yFloat];
}
The Function:
- (UIImage*) placeImageOnImage:(UIImage *)image topImage:(UIImage *)topImage x:(CGFloat *)x y:(CGFloat *)y {
// if you want the image to be added next to the image make this CGSize bigger.
CGSize newSize = CGSizeMake(image.size.width,image.size.height);
UIGraphicsBeginImageContext( newSize );
[topImage drawInRect:CGRectMake(*x,*y,topImage.size.width,topImage.size.height)];
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height) blendMode:kCGBlendModeDestinationOver alpha:1];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Looks OK. Perhaps you don't really need the CGFloat pointers, but that's fine, too.
The main idea is correct. There is no better way to do what you want.
Minuses:
1) Consider UIGraphicsBeginImageContextWithOptions method. UIGraphicsBeginImageContext isn't good for retina.
2) Don't pass floats as pointers. Use x:(CGFloat)x y:(CGFloat)y instead
You should use the begin context version, UIGraphicsBeginImageContextWithOptions, that allows you to specify options for scale (and pass 0 as the scale) do you don't lose any quality on retina displays.
If you want one image drawn on top of another image, just draw the one in back, then the one in front, exactly as if you were using paint. There is no need to use blend modes.
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
[topImage drawInRect:CGRectMake(*x,*y,topImage.size.width,topImage.size.height)];

How can I save a photo of part of the screen to the local iPhone's Photos?

I have put a UILabel that the user has chosen over a UIImageView that was also chosen by the user. I would like to put these two into a picture, kind of like a screenshot of a small part of the screen. I have absolutely no idea how to do this and have no experience in this. Any help is appreciated!!
You could setup a Bitmap context with a clipping mask of the area you want to save. Then you use the backing layer's renderInContext method to draw onto that context.
CGSize imageSize = CGSizeMake(960, 580);
UIGraphicsBeginImageContext(imageSize);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClipToRect(context, CGRectMake(10,10,200,200); // whatever rect you want
[self.layer renderInContext:context];
UIImage *myImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Save to camera roll
UIImageWriteToSavedPhotosAlbum(myImage, self, #selector(didSaveImage), null);

Duplicate UIViews

I'd like to display the same UIView multiple times. At the moment, I have my drawing in a primary UIView, then copy this into an image using renderInContext: and UIGraphicsGetImageFromCurrentImageContext. Then I set the contents of the other proxy UIViews to be this image.
UIGraphicsBeginImageContext(size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * clonedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return [clonedImage CGImage];
I'm experiencing a bottleneck in the renderInContext: call, presumably because it has to copy the image of the view. I'm seeing hot spots in resample_byte_h_3cpp and resample_byte_v_Ncpp, but I'm not sure what these are doing.
Is it possible to display the same UIView multiple times to reduce this overhead? Or is there a more efficient way to render the image?
How about making a copy of the UIImage instead of generating new images over and over from the UIView?
//.. Create the clonedImage from UIView
CGImageRef cgImageRef = [clonedImage CGImage];
UIImage *twinImage = [[UIImage alloc] initWithCGImage:cgImageRef];
//.. Use the images
[clonedImage release]; // if needed
[twinImage release]; // if needed