Why Does My Programmatically Created Screenshot Look So Bad on iOS 7

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

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)

iOS make screenshot of entire screen issue

You've created a loop of blurring. When you take the screenshot of the view a second time, the bluredImageView is also in the screenshot. That is why you see the effect multiplied. Try removing it and only capturing the context without the effect, then adding it back

 - (UIImage *)takeSnapshotOfView:(UIView *)view
{
//Remove the blured image before taking another screenshot.
[self bluredImageView] removeFromSuperview];
CGFloat reductionFactor = 1;

UIGraphicsBeginImageContext(CGSizeMake(view.frame.size.width/reductionFactor, view.frame.size.height/reductionFactor));
[view drawViewHierarchyInRect:CGRectMake(0, 0, view.frame.size.width/reductionFactor, view.frame.size.height/reductionFactor) afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

//Add it back now that the effect is done
[self addSubview:[self bluredImageView];
return image;
}

iOS: what's the fastest, most performant way to make a screenshot programmatically?

I've found a better method that uses the snapshot API whenever possible.

I hope it helps.

class func screenshot() -> UIImage {
var imageSize = CGSize.zero

let orientation = UIApplication.shared.statusBarOrientation
if UIInterfaceOrientationIsPortrait(orientation) {
imageSize = UIScreen.main.bounds.size
} else {
imageSize = CGSize(width: UIScreen.main.bounds.size.height, height: UIScreen.main.bounds.size.width)
}

UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
for window in UIApplication.shared.windows {
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
}

let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}

Wanna know more about iOS 7 Snapshots?

Objective-C version:

+ (UIImage *)screenshot
{
CGSize imageSize = CGSizeZero;

UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsPortrait(orientation)) {
imageSize = [UIScreen mainScreen].bounds.size;
} else {
imageSize = CGSizeMake([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width);
}

UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
CGContextSaveGState(context);
CGContextTranslateCTM(context, window.center.x, window.center.y);
CGContextConcatCTM(context, window.transform);
CGContextTranslateCTM(context, -window.bounds.size.width * window.layer.anchorPoint.x, -window.bounds.size.height * window.layer.anchorPoint.y);
if (orientation == UIInterfaceOrientationLandscapeLeft) {
CGContextRotateCTM(context, M_PI_2);
CGContextTranslateCTM(context, 0, -imageSize.width);
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
CGContextRotateCTM(context, -M_PI_2);
CGContextTranslateCTM(context, -imageSize.height, 0);
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
CGContextRotateCTM(context, M_PI);
CGContextTranslateCTM(context, -imageSize.width, -imageSize.height);
}
if ([window respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
} else {
[window.layer renderInContext:context];
}
CGContextRestoreGState(context);
}

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

Why does view.layer.renderInContext() take higher amount of temporary memory for same size screenshot

UIWebView internally uses CATiledLayer to render the web page. Depending on the website being rendered a lot of internal sub layers are created by UIWebView. When we try to render the UIWebView contents in Image context, recursive calls are made to take a screenshot of all the layers.

Thus depending on how the UIWebView builds the CATiledLAyer internally the temporary memory used by the screenshot code varies widely ( 60MB - >200MB)

UIGraphics crashes on iOS 7

OK, Finally I got it working. That was a nice learning experience overall :).

Actually the very nature of "EXE_BAD_ACCESS" did hint towards bad memory management, i.e. I was requesting access to something non-existent. Unfortunately, (or quite logically, which I missed earlier) leaks would not find it. But they were caught when I profiled my app for zombies.

The problem occurred due to the method

   [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];   // iOS 7

or equivalently in iOS 6

   [self.layer renderInContext:UIGraphicsGetCurrentContext()];

The sequence of my app progress was something like this:

   render view -> update a few things -> request a screenshot be taken on update
-> update the view -> return to previous view (releasing this one)

Now, because I demanded the screen shots be taken upon update, these methods waited till the view update occurs. However, immediately after the update I was releasing the superview. And hence these methods (after waiting for the update) called this view after it was released.

Now, I don't know if this is Apple's iOS bug or my poor understanding of it. But now, I don't release the superview immediately upon view update and everything works fine :).

Thanks guys for your help. Let me know if I am doing something odd here and this behavior can be prevented in a more efficient way.

best,
Nikhil



Related Topics



Leave a reply



Submit