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 unmodifiablecontent
resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets
: same thing, but with resizable insetsdrawViewHierarchyInRect: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
Uibezierpath Triangle with Rounded Edges
Presenting Camera Permission Dialog in iOS 8
Swift Performselector:Withobject:Afterdelay: Is Unavailable
Swift - Uibutton with Two Lines of Text
How to Set Kerning in iPhone Uilabel
Checking for Multiple Asynchronous Responses from Alamofire and Swift
How to Change Uitableviewrowaction Title Color
Spritekit - Not Loading @3X Images from Sktextureatlas
Firebase Cloud Messaging Doesn't Create Push Notifications But Gets Information
Simple Way to Change the Position of Uiview
iOS 5.1 with Xcode 4.2 and Retina in iPad 3
Xcode 8 "The Aps-Environment Entitlement Is Missing from the App's Signature" on Submit
Making a Phone Call in an iOS Application
iOS Enterprise Ota Distribution Unable to Download Application
How to Keep a Round Imageview Round Using Auto Layout