How do I take a full screen Screenshot in Swift?
Solution 1:
Instead of just throwing the answer out there, let me explain what your current code does and how to modify it to capture the full screen.
UIGraphicsBeginImageContext(view.frame.size)
This line of code creates a new image context with the same size as view
. The main thing to take away here is that the new image context is the same size as view
. Unless you want to capture a low resolution (non-retina) version of your application, you should probably be using UIGraphicsBeginImageContextWithOptions
instead. Then you can pass 0.0
to get the same scale factor as the devices main screen.
view.layer.renderInContext(UIGraphicsGetCurrentContext())
This line of code will render the view's layer into the current graphics context (which is the context you just created). The main thing to take away here is that only view
(and its subviews) are being drawn into the image context.
let image = UIGraphicsGetImageFromCurrentImageContext()
This line of code creates an UIImage object from what has been drawn into the graphics context.
UIGraphicsEndImageContext()
This line of code ends the image context. It's clean up (you created the context and should remove it as well.
The result is an image that is the same size as view
, with view
and its subviews drawn into it.
If you want to draw everything into the image, then you should create an image that is the size of the screen and draw everything that is on screen into it. In practice, you are likely just talking about everything in the "key window" of your application. Since UIWindow
is a subclass of UIView
, it can be drawn into a image context as well.
Solution 2:
Swift 4
/// Takes the screenshot of the screen and returns the corresponding image
///
/// - Parameter shouldSave: Boolean flag asking if the image needs to be saved to user's photo library. Default set to 'true'
/// - Returns: (Optional)image captured as a screenshot
open func takeScreenshot(_ shouldSave: Bool = true) -> UIImage? {
var screenshotImage :UIImage?
let layer = UIApplication.shared.keyWindow!.layer
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
guard let context = UIGraphicsGetCurrentContext() else {return nil}
layer.render(in:context)
screenshotImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
if let image = screenshotImage, shouldSave {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
return screenshotImage
}
Updated for Swift 2
The code you provide works but doesn't allow you to capture the NavigationBar
and the StatusBar
in your screenshot. If you want to take a screenshot of your device that will include the NavigationBar
, you have to use the following code:
func screenShotMethod() {
let layer = UIApplication.sharedApplication().keyWindow!.layer
let scale = UIScreen.mainScreen().scale
UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
layer.renderInContext(UIGraphicsGetCurrentContext()!)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(screenshot, nil, nil, nil)
}
With this code:
- The first time you launch the app and call this method, the iOS device will ask you the permission to save your image in the camera roll.
- The result of this code will be a .JPG image.
- The
StatusBar
will not appear in the final image.
Solution 3:
Details
- Xcode 9.3, Swift 4.1
- Xcode 10.2 (10E125) and 11.0 (11A420a), Swift 5
Tested on iOS: 9, 10, 11, 12, 13
Solution
import UIKit
extension UIApplication {
func getKeyWindow() -> UIWindow? {
if #available(iOS 13, *) {
return windows.first { $0.isKeyWindow }
} else {
return keyWindow
}
}
func makeSnapshot() -> UIImage? { return getKeyWindow()?.layer.makeSnapshot() }
}
extension CALayer {
func makeSnapshot() -> UIImage? {
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(frame.size, false, scale)
defer { UIGraphicsEndImageContext() }
guard let context = UIGraphicsGetCurrentContext() else { return nil }
render(in: context)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
return screenshot
}
}
extension UIView {
func makeSnapshot() -> UIImage? {
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(size: frame.size)
return renderer.image { _ in drawHierarchy(in: bounds, afterScreenUpdates: true) }
} else {
return layer.makeSnapshot()
}
}
}
extension UIImage {
convenience init?(snapshotOf view: UIView) {
guard let image = view.makeSnapshot(), let cgImage = image.cgImage else { return nil }
self.init(cgImage: cgImage, scale: image.scale, orientation: image.imageOrientation)
}
}
Usage
imageView.image = UIApplication.shared.makeSnapshot()
// or
imageView.image = view.makeSnapshot()
// or
imageView.image = view.layer.makeSnapshot()
// or
imageView.image = UIImage(snapshotOf: view)
Old solution
Xcode 8.2.1, swift 3
Version 1 for iOS 10x
import UIKit
extension UIApplication {
var screenShot: UIImage? {
if let layer = keyWindow?.layer {
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
if let context = UIGraphicsGetCurrentContext() {
layer.render(in: context)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return screenshot
}
}
return nil
}
}
Version 2 for iOS 9x, 10x
If you will try to use Version 1 code in iOS 9x you will have error: CGImageCreateWithImageProvider: invalid image provider: NULL.
import UIKit
extension UIApplication {
var screenShot: UIImage? {
if let rootViewController = keyWindow?.rootViewController {
let scale = UIScreen.main.scale
let bounds = rootViewController.view.bounds
UIGraphicsBeginImageContextWithOptions(bounds.size, false, scale);
if let _ = UIGraphicsGetCurrentContext() {
rootViewController.view.drawHierarchy(in: bounds, afterScreenUpdates: true)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return screenshot
}
}
return nil
}
}
Usage
let screenShot = UIApplication.shared.screenShot!
Solution 4:
Swift UIImage
extension:
extension UIImage {
convenience init?(view: UIView?) {
guard let view: UIView = view else { return nil }
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
guard let context: CGContext = UIGraphicsGetCurrentContext() else {
UIGraphicsEndImageContext()
return nil
}
view.layer.render(in: context)
let contextImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard
let image: UIImage = contextImage,
let pngData: Data = image.pngData()
else { return nil }
self.init(data: pngData)
}
}
Usage:
let myImage: UIImage? = UIImage(view: myView)
Solution 5:
For ease I would add an extension in it's own file
import UIKit
public extension UIWindow {
func capture() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.frame.size, self.opaque, UIScreen.mainScreen().scale)
self.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
call the extension as follows...
let window: UIWindow! = UIApplication.sharedApplication().keyWindow
let windowImage = window.capture()
Similarly, one could extended UIView to capture an image of that....