How can I take a snapshot of a UIView that isn't rendered?
drawViewHierarchyInRect
will work for you. You can use this directly on your WKWebView.
There is some useful detail on this Apple Technical Q&A. Also I touch on it here in answer to a slightly different question.
I use it in a category on UIView:
@implementation UIView (ImageSnapshot)
- (UIImage*)imageSnapshot {
UIGraphicsBeginImageContextWithOptions(self.bounds.size,
YES, self.contentScaleFactor);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end
I don't know what you mean by 'the obvious method' - but I have tested this on a WKWebView and it works.
Capture a Screen Shot of UIView - Slow Performance
Did you use the "Time Profiler" Instrument ("Product" Menu -> "Profile") to check where in your code you spend the most of your time? (use it with your Device of course, not the Simulator, to have realistic profiling). I'd guess it is not in the image capture portion you quoted in your question, but in your rescaling method imageWithImage:scaledToSize:
method.
Instead of rendering the image at its whole size in a context, then rescaling the image to the final size, you should render the layer in the context directly at the expected size by applying some affine transform to the context.
So simply use CGContextConcatCTM(someScalingAffineTransform);
on UIGraphicsGetCurrentContext()
right after your line UIGraphicsBeginImageContext(size);
, to apply an scaling affine transform that will make the layer be rendered at a different scale/size.
This way it will be directly rendered as the expected size which will be much faster, instead of being rendered at 100% and then having you to rescale it afterwards in a time-consuming way
How to convert a UIView to an image
An extension on UIView
should do the trick.
extension UIView {
// Using a function since `var image` might conflict with an existing variable
// (like on `UIImageView`)
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
Apple discourages using UIGraphicsBeginImageContext
starting iOS 10 with the introduction of the P3 color gamut. UIGraphicsBeginImageContext
is sRGB and 32-bit only. They introduced the new UIGraphicsImageRenderer
API that is fully color managed, block-based, has subclasses for PDFs and images, and automatically manages the context lifetime. Check out WWDC16 session 205 for more details (image rendering begins around the 11:50 mark)
To be sure that it works on every device, use #available
with a fallback to earlier versions of iOS:
extension UIView {
// Using a function since `var image` might conflict with an existing variable
// (like on `UIImageView`)
func asImage() -> UIImage {
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
} else {
UIGraphicsBeginImageContext(self.frame.size)
self.layer.render(in:UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImage(cgImage: image!.cgImage!)
}
}
}
Snapshot scaled UIView
To draw the complete content view of a scroll view you could use something like:
@IBAction func onClear(_ sender: Any) {
self.targetImageView.image = nil
}
@IBAction func onSnapshot(_ sender: Any) {
UIGraphicsBeginImageContextWithOptions(self.contentView.bounds.size, false, UIScreen.main.scale)
if let context = UIGraphicsGetCurrentContext() {
self.contentView.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
self.targetImageView.image = image
} else {
self.targetImageView.image = nil
}
UIGraphicsEndImageContext()
}
Test
In the upper area there is a scroll view with activated zoom (maximumZoomScale = 5). In the lower area there is only a simple UIImageView. If one taps on the button with the label "snap", onSnapshot
is called and the lower UIImageView is filled with the snapshot image. With the button 'clear' UIImageView's image is set to nil.
Related Topics
Extract Last Word in String with Swift
Allow "Auto Lock" While Video Is Being Played
Two Buttons Inside Hstack Taking Action of Each Other
Nspredicate Filtered by Year Moth Day
Getting the Local iPhone Number Through Sdk
What Is Difference Between Self.Timer = Nil VS [Self.Timer Invalidate] in iOS
Using Uisheetpresentationcontroller in Swiftui
Displaying Text One Character at a Time in Swift 2.0
Firebase Push Notifications Custom Sound
How to Change the Height of Uitextfield in Uialertcontroller in Swift
Mkmapview Doesn't Zoom Correctly While User Tracking Mode Is Mkusertrackingmodefollowwithheading
How Can One Enable Keyboard Like in Imessages/Fb Messenger in Landscape Mode at iOS8
Xcode:Why Launchoptions in Didfinishlaunchingwithoptions Always Nil
iOS Notification When Application Is Closed
What Is the Use of Singleton Class in Objective-C
Multiple Uialertcontrollers in iOS