How to optimize memory usage in UIImage - ios

I try to take screenshot of uiwebview and send it with observer to another UIImageView in another class.
I using this method to take screenshot:
-(UIImage*)takeScreenshoot{
#autoreleasepool {
UIGraphicsBeginImageContext(CGSizeMake(self.view.frame.size.width,self.view.frame.size.height));
CGContextRef context = UIGraphicsGetCurrentContext();
[self.webPage.layer renderInContext:context];
UIImage *__weak screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenShot;
}
}
But then I have problem. Everytime I take screenshot with this method, memory rate grows about 10-15mb and it's not realising it. And if I take screenshot in every webviewdidfinishload, you can imagine how much it can take memory!
How can I fix that issue?

If possible try to use [UIScreen snapshotViewAfterScreenUpdates] which returns UIView .
This is the snapshot of currently displayed content (snapshot of app).
Even apple also says " this method is faster than trying to render the contents of the screen into a bitmap image yourself."
According to your code, you are passing this bitmap image to just display in some other UIImageView. so i think using UIScreen method is appropriate here.
To display UIWebView part only->
Create another UIView instance. Set its frame to the frame of your webView.
Now add this screenShot view as subView to createdView and set its frame such that webView portion will be displayed.

Try calling CGContextRelease(context); after you have got your screen shot.
Or as #Greg said, remove the line and use UIGraphicsGetCurrentContext() directly

Related

Screenshot at required CGRect on UIView

I made a 3x3 cell layout with UIViews. So there are 9 UIViews!!!
I am trying to screenshot these 9 UIviews individually. But Whatever I tried, I can only screenshot the 1st UIview.
HEre is the screenshot of the main view that holds the 9 subviews:
And I want to screenshot the second tile by sending _tile2, but the final result I get is _tile1 only.
:
Here is the code:
[self saveImage:[self screenshotTile:_tile2]]; //_tile1,_tile2...._tile9 gave the same result
What I believe is If I send the _tile1, it should screenshot the _tile1 area on the self.view, and If I send the _tile2, it should screenshot the _tile2 area on the self.view .But whatever I send, it only captures the _tile1 area on the view.
-(void)saveImage:(UIImage *)img{
UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil);
}
-(UIImage *)screenshotTile :(UIView *)imgV{
UIGraphicsBeginImageContext(imgV.frame.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
_tileImg1 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return _tileImg1;
}
I tried this solution which suggests to shift the image after taking the screenshot, but it didnt work too:
UIGraphicsBeginImageContext(sshot.frame.size);
[sourceImage drawAtPoint:CGPointMake(-50, -100)]; // I tried -imgV.frame.origin.v, -imgV.frame.origin.y but it didnt work for me!!!
You pass a tile to the screenshotTile: method. In that method, you never actually use the tile. Maybe you want to send renderInContext: to imgV.layer, not to self.view.layer.
After few trials, I managed to get it worked by using this code:
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(c, -imgV.frame.origin.x, -imgV.frame.origin.y);
[self.view.layer renderInContext:c];
I need to shift the context x axis and y axis according to the tile frame. The context always starts from the 0,0 point, So I need to move the image according to the tile frame.
In the process of doing this, I encountered one wierd error which stopped me in saving the images to the camera roll:
Connection to assetsd was interrupted or assetsd died
I am not sure why and how it occured. After few trials, the error disappeared. I dont even know how ti reproduce it!!!!!

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.

Why does my programmatically created screenshot look so bad on iOS 7?

I am trying to implement sharing app with facebook.
I used this code to take the screenshot:
CGSize imageSize = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height);
UIGraphicsBeginImageContext(imageSize);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
It works great on iOS 6, but in iOS 7 the image looks very bad.
I used this answer: iOS: what's the fastest, most performant way to make a screenshot programmatically?
for trying to fix it, and it helped, still the screenshot looks bad.
The screen gets another color, and some objects (like labels) aren't showing on the image taken.
Any help?
----Update----
I managed to solve the most objects, by change them to retain instead of weak. My main problem remained my tableview, that shown as a big white block (It supposed to be transparent, with labels with white text, so all we see is white cells). I did try to define the table background as clearcolor,not helps..
----Last Update---
There are wonderful answers here that not really regarding to my issue.. I wanted to make it work on device that runs with iOS7 but without using iOS7 SDK, since it takes to much effort to switch the project SDK in this point, when the project is almost done.
Anyway, I added the peace of code that finally solved my issue:
This change simply solve the problem:
UIGraphicsBeginImageContextWithOptions(imageSize, NO , 0.0f);
instead of:
UIGraphicsBeginImageContext(imageSize);
New API has been added since iOS 7, that should provide efficient way of getting snapshot
snapshotViewAfterScreenUpdates: renders the view into a UIView with unmodifiable content
resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets : same thing, but with resizable insets
drawViewHierarchyInRect:afterScreenUpdates: : same thing if you need all subviews to be drawn too (like labels, buttons...)
You can use the UIView returned for any UI effect, or render in into an image like you did if you need to export.
I don't know how good this new method performs VS the one you provided (although I remember Apple engineers saying this new API was more efficient)
you can try this
- (UIImage *) screenshot {
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, [UIScreen mainScreen].scale);
[self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
in iOS 8 : this is how i am doing to get ScreenShot
just added one UIImageView and Method to take screenshot in .h file
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
-(IBAction)takeSnapShot:(id)sender;
2 added code snip for taking screen shot and set on UIImageView in .m file
- (IBAction)takeSnapShot:(id)sender
{
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *snapShotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
imageView.image = snapShotImage;
}
below is the output i got.
On iOS7 you can have glitches if you use
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES]
during ongoing animation. Set afterScreenUpdates = NO to get rid of glitches.
Make sure that opaque is set to NO

Taking a Screenshot (UIImage) from UIView takes far too long

I have the following method to take a screenshot (UIImage) of a UIView which is far too slow
+ (UIImage *)imageWithView:(UIView *)view
{
CGSize size = view.bounds.size;
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
return image;
}
On my iPad I now have an app that needs this method to make a copy of a view that is drag&dropped. This view is one with rounded corners and therefore is not opaque (which not makes a difference to if I would set the isOpaque param to YES I found out)...
Also the view that is screenshotted contains a UITableView with quite some complex entries in it...
Do you have any suggestions on how I can improve the speed of the screenshotting. Right now, for a bit bigger tableview (maybe 20 entries) it takes about 1 second (!!!)
And the view is already on screen, rendered correctly... so I just need the Pixels to but into an UIImageView...
I need to support iOS 6+.
I use this same code to take a screenshot of a really complex views. I think your bottleneck is using a big image for the drag&drop. Maybe you can resize the UIImage.
In my case the performance in a iPad2 is about 100ms for screenshot.

Draw text and add to a UIImage iOS 5/6

I have a textbox, where i want the written text to be added to a UIImage.
How can i draw NSString to a UIImage?
I´ve searched, and found lots of examples, but non of them works. Xcode just gives me lots of errors.
Simply put, i want to draw a NSString to a UIimage. The UIImage should be the same size as a predefined UIImageView, and be placed in center.
Any ideas?
i think solution is here..i have used this method in one of my application
here lbltext is the object of my label. this method creates new image with given text & return the object of new image with text.
-(UIImage *) drawText:(NSString*) text inImage:(UIImage*)image atPoint:(CGPoint)point
{
UIFont *font = lblText.font;
UIGraphicsBeginImageContext(iv.frame.size);
[image drawInRect:CGRectMake(0,0,iv.frame.size.width,iv.frame.size.height)];
CGRect rect = CGRectMake(point.x,point.y,lblText.frame.size.width, lblText.frame.size.height);
[lblText.textColor set];
[text drawInRect:CGRectIntegral(rect) withFont:font];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
in your below comment you have passed iv.center as a point.. try manualy like this
CGPoint point;
point.x=30; // dynamicaly just for example lblText.frame.origin.x;
point.y=40; //lblText.frame.origin.y;
you have to call above method like this...
UIImage *img = [self drawText:#"Test String" inImage:originalImage atPoint:point];
here,img is another instace of UIImage for storing new image with text...after calling this method use img imager for your further use..
for example...
[newimageviewObj setImage:img];
i hope this will help you
UIImage is not a subview of UIView, so you cant add a subview to it. Also NSString is not a subview of UIView. If you want to show things on the screen, they should inherit from UIView.
So try this:
Create a UIImageView - set its image property to be your UIImage instance.
Create a UILabel - set its text property to your NSString.
Add the UILabel as a subview of your UIImageView.
and finally add your UIImageView to your current view controllers view.
Emm, here is some thoughts.
I think that one simple way is like this :
Put aUIImageView on aView;
Add aUITextView on aView;
Get ScreenShot from aView;
This may works fine.
Also, this may come with a problem that screenshot may be not clear.
Then, After step1 and step2, we may get new image by UIGraphics.
(With here, we already know position where text should be on image and this should be ok.)
PS: Some image may not have some attribution like CGImage and CGImage is needed for this. So, transform it.

Resources