CALayer renderInContext: causing unknown crash - ios

The entire block of code consists of the following:
CGSize layerSize = [webview sizeThatFits:CGSizeZero];
if ([UIScreen instancesRespondToSelector:#selector(scale)] && [[UIScreen mainScreen] scale] == 2.0f) {
UIGraphicsBeginImageContextWithOptions(layerSize, NO, 2.0f);
}
else {
UIGraphicsBeginImageContext(layerSize);
}
[webview.layer renderInContext:UIGraphicsGetCurrentContext()];
screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
but after testing, this line is the one causing the problem:
[webview.layer renderInContext:UIGraphicsGetCurrentContext()];
The app crashes with no reason listed in the console, and using #try #catch #finally comes up with nothing. I imported Quartzcore in AppDelegate.h, if that has anything to do with it. The app works fine in the simulator, but crashes when run on a real device.

#Greg : seems like a memory overflow issue on device since device is memory constrained while simulator runs with different memory configuration , i am running into the same - this can happen for long web pages , any ideas how to solve it ?
does anyone what is max width and height [CALayer renderInContext] can handle on actual device (iphone retina or non-retina) before it crashes ?

try
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

Related

UIPickerView get darker on screenshot

I had to alter the navigation on certain circumstances, and due to complexity of the transitions I had take an paint and screenshot until the transition is finished. In almost cases, that works pretty well, but there is a point that disturb me. I have a view controller with two picker views:
But the screenshot is not working well on this VC. I get this:
The code that takes the screenshot is the following in both cases:
- (UIImage *)takeScreenshot {
CALayer *layer = [[UIApplication sharedApplication] keyWindow].layer;
UIGraphicsBeginImageContext(layer.frame.size);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
return screenshot;
}
Anyone knows how could be happened?
You could try to use a different method for screenshot. Apple introduced in iOS 7 some methods for fast view screenshot.
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
[self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES];
UIImage *im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Here is an answer from Apple that provides more info on how the 2 methods works. While the respective user encountered some pb and was advised to use the old way of snapshotting the view, I never had any problem with it. Maybe they fixed it since then.
UIGraphicsBeginImageContext(self.window.bounds.size);
[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * data = UIImagePNGRepresentation(image);
[data writeToFile:#"image.png" atomically:YES];
if you have a retina display then replace the first line with the below code:-
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)])
UIGraphicsBeginImageContextWithOptions(self.window.bounds.size, NO,[UIScreen mainScreen].scale);
else
UIGraphicsBeginImageContext(self.window.bounds.size);

VM: CG raster Data memory keep growing

so i am trying to make an app that will let the user change the color of the UIImage, for that i am using this function i found
- (UIImage *)imageWithTintColor:(UIColor *)color fraction:(CGFloat)fraction
{
if (color)
{
UIImage *image;
if ([UIScreen instancesRespondToSelector:#selector(scale)])
{
UIGraphicsBeginImageContextWithOptions([self size], NO, 0.f);
}
else
{
UIGraphicsBeginImageContext([self size]);
}
CGRect rect = CGRectZero;
rect.size = [self size];
[color set];
UIRectFill(rect);
[self drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0];
if (fraction > 0.0)
{
[self drawInRect:rect blendMode:kCGBlendModeSourceAtop alpha:fraction];
}
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
return self;
}
everything works but the CG raster Data is growing in memory
I found the problem, and it was my bad logic, i am using 2 views one to show and one to work with ex:resize, move, rotate. And each time i was addingSubview to both where one of them need to hold just 1 at a time, a simple:
for (UIView *view in 2cndView.subviews)
{
[view removeFromSuperview];
}
did the trick for me
I have been fighting with my app, that suddenly would not launch properly, for some time now. It turned out that when I had switched a number of images' Render as to Template in the Image asset file, it caused the app to totally bomb out. CG Raster Data was growing exponentially and finally caused the app to stop and Xcode just said
Lost connection with iPhone.. check connections etc
It would appear that during every launch the images get reprocessed for this 'Template' setting, which consumed a disgusting amount of RAM and actually left it unable to boot. To solve this, I lowered the resolution of the images - as simple as that.

UIGraphics drawInRect: crashes on 4S device

Basically, i do:
- (UIImage *)addPreviewImageToImage:(UIImage *)backImage
{
UIImage *newImage;
NSString *file = [[NSBundle mainBundle] pathForResource:#"sampleImage" ofType:#"png"];
UIImage *frontImage = [UIImage imageWithContentsOfFile:file];
float scale = 1.0f;
CGRect rectBack = CGRectMake(0, 0, scale * backImage.size.width, scale * backImage.size.height);
CGRect rectFront = [self rectForFrontElementOfSize:frontImage.size overElementOfSize:rectBack.size mediaType:kMediaTypeImage];
// Begin context
UIGraphicsBeginImageContextWithOptions(rectBack.size, YES, 0); // opaque = YES
// draw images
#autoreleasepool { // no effect
[backImage drawInRect:rectBack]; // where the crash occurs, backImage is about 4 Mo
}
[frontImage drawInRect:rectFront];
// grab context
newImage = UIGraphicsGetImageFromCurrentImageContext();
// end context
UIGraphicsEndImageContext();
return newImage;
}
This works fine on iPhone 5 or more (iOS7) but crashes 80% of the time on 4S on drawInRect: call.
How can I optimize this code ? Would writing the backImage on the disk and then get it via contentsOfFile be more efficient ? Any ideas ?
EDIT (crash details):
After receiving memory warnings, Xcode terminates the app stating :
"Terminated due to Memory Pressure"
By using breakpoints I know this occurs when executing drawInRect:backImage (which is a picture from the camera)
rectBack description before crash:
(CGRect) rectBack = origin=(x=0, y=0) size=(width=2448, height=3264)
Note:
Having set opaque argument as YES in UIGraphicsBeginImageContextWithOptions and reducing the scale argument seems to make it work, but I am loosing quality and I'd like to know if there's something wrong in the logic

iOS8 scale glitch when calling drawViewHierarchyInRect afterScreenUpdates:YES

I was converting a project from iOS7 to iOS8 which uses custom transitions and needs to capture the modal after it finishes loading afterScreenUpdates:YES and was seeing that the entire screen scale up for a second and scale back down. I also see this happening in the Flickr app for iOS between sections and on Yelp app when transitioning to a photo on iOS8.
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 22.0);
[self.view drawViewHierarchyInRect:self.view.frame afterScreenUpdates:YES];
UIGraphicsEndImageContext();
Adding a larger scale factor helps emphasize the glitch more... but i'm just calling this on a button press in the example.
EDIT This appears to happen on iPhone 6 and 6 plus not on the 5.
Sample project github
Do you NEED it to draw after the screen updates? because I'm using:
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
and it seems to work fine on iOS7 and iOS8. I imagine this isn't a great solution for capturing images regularly (like multiple times a second) but it seems to work for a once off blur.
You have to provide #3x launch images for the 6 and 6 plus. The 6 being scaled at 750x1334, and the 6 plus image being scaled at 1242x2208.
Even it looks like bug in API, you can call drawViewHierarchyInRect with afterScreenUpdates set to NO to build snapshot AFTER screen updates if you use cunstruction like this:
typedef void (^CompletionHandlerWithId)(id result);
-(void)imageContaining:(CGRect)rect afterScreenUpdates:(bool)afterScreenUpdates opaque:(BOOL)opaque completion:(CompletionHandlerWithId)completion
{
bool success __block;
UIImage *snapshotImage __block = nil;
CompletionHandler block = ^{
// Create the image
UIGraphicsBeginImageContextWithOptions(self.bounds.size, opaque, [[UIScreen mainScreen] scale]);
success = [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
if (success)
{
snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
CGImageRef imageRef = CGImageCreateWithImageInRect(
[snapshotImage CGImage],
CGRectMake(
snapshotImage.scale*rect.origin.x,
snapshotImage.scale*rect.origin.y,
snapshotImage.scale*rect.size.width,
snapshotImage.scale*rect.size.height));
// or use the UIImage wherever you like
snapshotImage = [UIImage imageWithCGImage:imageRef scale:snapshotImage.scale orientation:UIImageOrientationUp];
CGImageRelease(imageRef);
}
UIGraphicsEndImageContext();
if (completion)
{
if (! success)
{
NSLog(#"Error: [UIView drawViewHierarchyInRect] failed on %#", self);
(completion)(nil);
}
else
{
NSLog(#"Success: [UIView drawViewHierarchyInRect] on %#", self);
(completion)(snapshotImage);
}
}
};
if (afterScreenUpdates)
[CATransaction setCompletionBlock:^{
(block)();
}];
else
(block)();
}
This bug also exists when you run on an iPad2 running iOS7.
Fix: set afterScreenUpdates: to NO
My app has some moving UIButtons, so I don't allow the blur transition until after the movement has stopped. As far as I have found so far, there is no difference in YES or NO.
Appears to be fixed in iOS9 / XCODE 7 builds
I found a solution for me to solve this problem.
I add #3x launch images to my project. And choose launch Screen file to "Main".
This will make app run at original resolution, smaller bounds when run at iphone6, but not glitch when calling drawViewHierarchyInRect. Like this.
Then, scale my view to fullscreen when viewDidLoad.
- (void)viewDidLoad{
[super viewDidLoad];
UIScreen *mainScreen = [UIScreen mainScreen];
CGRect tempFrame=mainScreen.bounds;
double aspect=tempFrame.size.width/320;
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, aspect, aspect);
}
Hope helpful :)

UIGraphicsBeginImageContextWithOptions very masive memory disaster

I have this line of code to take screenshot of uiview:
- (UIImage *)imageWithView:(UIView *)view {
UIImage *viewImage = nil;
float height;
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight){
height = view.frame.size.height;
}else{
height = 730;
}
UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.frame.size.width, height), YES, 0.0);
[view drawViewHierarchyInRect:CGRectMake(0, -60, view.bounds.size.width, height) afterScreenUpdates:YES];
viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
... in this uiview is UIWebView.
This - (UIImage *)imageWithView:(UIView *)view method I call in everytime uiwebview finish load. And there's is my problem: When I open some small webpages like google it method takes some kb of memory, but when I open something biger like bbc, cnn, yahoo or stackoverflow, it can take up to 80mb memory usage.
There is snapshoot of instrument when cnn.com was opened.
After few seconds it realeses, but I don't want that big memory usage, because you might imagine, how useless becomes uiwebview in these seconds.
So, what is your suggestions, how to take screenshoot of uiwebview, without so big memory usage, I don't even need this uiimage in good quality, because I put it in 120*80 uiimageview in one of the screen corners.
The last parameter in UIGraphicsBeginImageContextWithOptions is scale. If you know you only need a low resolution rendering of the web view, try setting a value for scale less than 1.0.
UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.frame.size.width, height), YES, 0.2);

Resources