How do I take a full screen Screenshot in Swift?

IosSwiftUiimageScreenshot

Ios Problem Overview


I've found this code:

func screenShotMethod() {
    //Create the UIImage
    UIGraphicsBeginImageContext(view.frame.size)
    view.layer.renderInContext(UIGraphicsGetCurrentContext())
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    //Save it to the camera roll
    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}

What do I need to do to get all the other elements, like the navigation bar, into the screenshots?

Ios Solutions


Solution 1 - Ios

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 - Ios

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 - Ios

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 - Ios

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 - Ios

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....

Solution 6 - Ios

The recommended way to create a context in iOS 10 is to use UIGraphicsImageRenderer.

extension UIView {
	func capture() -> UIImage? {
		var image: UIImage?

		if #available(iOS 10.0, *) {
			let format = UIGraphicsImageRendererFormat()
			format.opaque = isOpaque
			let renderer = UIGraphicsImageRenderer(size: frame.size, format: format)
			image = renderer.image { context in
				drawHierarchy(in: frame, afterScreenUpdates: true)
			}
		} else {
			UIGraphicsBeginImageContextWithOptions(frame.size, isOpaque, UIScreen.main.scale)
			drawHierarchy(in: frame, afterScreenUpdates: true)
			image = UIGraphicsGetImageFromCurrentImageContext()
			UIGraphicsEndImageContext()
		}

		return image
	}
}

Solution 7 - Ios

I use this method:

func captureScreen() -> UIImage {
    var window: UIWindow? = UIApplication.sharedApplication().keyWindow
    window = UIApplication.sharedApplication().windows[0] as? UIWindow
    UIGraphicsBeginImageContextWithOptions(window!.frame.size, window!.opaque, 0.0)
    window!.layer.renderInContext(UIGraphicsGetCurrentContext())
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image;
}

It captures all but the status bar, and it doesn't ask for permission to save images in the camera roll.

Hope it helps!

Solution 8 - Ios

Works with Swift 5 and iOS 13

For anyone looking for a quick answer for a function to return a screenshot of the view as a UIImage:

func getScreenshot() -> UIImage? {
	//creates new image context with same size as view
	// UIGraphicsBeginImageContextWithOptions (scale=0.0) for high res capture
	UIGraphicsBeginImageContextWithOptions(view.frame.size, true, 0.0)
	
	// renders the view's layer into the current graphics context
	if let context = UIGraphicsGetCurrentContext() { view.layer.render(in: context) }
	
	// creates UIImage from what was drawn into graphics context
	let screenshot: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
	
	// clean up newly created context and return screenshot
	UIGraphicsEndImageContext()
	return screenshot
}

I pieced together this answer from taking the code in the question and following David Rönnqvist's suggestions (thank you for the explanation), with some tweaks.

To include the nav bar and other extras, call this method off of the window instead of the view.

I simply needed a function to get the view's screen capture, so I hope this helps anyone looking for the same

Solution 9 - Ios

Swift 3 Extension for UIWindow

public extension UIWindow {

  func capture() -> UIImage? {
    
    UIGraphicsBeginImageContextWithOptions(self.frame.size, self.isOpaque, UIScreen.main.scale)
    self.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return image

  }
}]

Solution 10 - Ios

This is how I do it in Swift 4

let layer = UIApplication.shared.keyWindow!.layer
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);                 
layer.render(in: UIGraphicsGetCurrentContext()!)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

now screenshot will be type UIImage

Solution 11 - Ios

Swift 5

If you just need a true snapshot of the screen (with keyboard and status bar) in that instant:

let snap = UIScreen.main.snapshotView(afterScreenUpdates: false)

snap is a UIView with a frame automatically set to the bounds of the screen. Adding it to the view of a view controller will now give you a UI that appears frozen:

view.addSubview(snap)

No need for extensions and all that unnecessary stuff—2 lines of code is enough.

Solution 12 - Ios

Swift 5

Captures entire screen (minus status bar) without crashing on newer devices (like iPhone 12 Pro).

extension UIApplication {
    func getScreenshot() -> UIImage? {
        guard let window = keyWindow else { return nil }
        let bounds = UIScreen.main.bounds
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0)
        window.drawHierarchy(in: bounds, afterScreenUpdates: true)
        guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
        UIGraphicsEndImageContext()
        return image
    }
}

Previous solutions that use UIApplication.shared.keyWindow?.layer.render(in: context) causes a memory crash on some devices like iPhone 12 Pro.

Solution 13 - Ios

  // Full Screen Shot function. Hope this will work well in swift.
  func screenShot() -> UIImage {                                                    
    UIGraphicsBeginImageContext(CGSizeMake(frame.size.width, frame.size.height))
    var context:CGContextRef  = UIGraphicsGetCurrentContext()
    self.view?.drawViewHierarchyInRect(frame, afterScreenUpdates: true)
    var screenShot = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext();  
    return screenShot
  }

Solution 14 - Ios

This is similar, hopefully it helps someone in the future.

self.view.image() //returns UIImage

Here's a Swift 3 solution

https://gist.github.com/nitrag/b3117a4b6b8e89fdbc12b98029cf98f8

Solution 15 - Ios

This code is the latest version - 100% works

func getScreenshoot() -> UIImage {
    var window: UIWindow? = UIApplication.shared.keyWindow
    window = UIApplication.shared.windows[0] as? UIWindow
    UIGraphicsBeginImageContextWithOptions(window!.frame.size, window!.isOpaque, 0.0)
    window!.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!;
}

Solution 16 - Ios

> view.snapshotView(afterScreenUpdates: true)

Solution 17 - Ios

My version also capture a keyboard. Swift 4.2

extension UIApplication {
    
    var screenshot: UIImage? {
        UIGraphicsBeginImageContextWithOptions(UIScreen.main.bounds.size, false, 0)
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        for window in windows {
            window.layer.render(in: context)
        }
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
    
}

Solution 18 - Ios

Swift 4 or above.

For extension and call by UIView that you capture.

Declaration

extension UIView {

    func viewCapture() -> UIImage? {

        UIGraphicsBeginImageContext(self.frame.size)

        guard let cgContext = UIGraphicsGetCurrentContext() else {
        print("Fail to get CGContext")
        return nil

    }
    self.layer.render(in: cgContext)

    guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
        print("Fail to get Image from current image context")
        return nil
    }
    UIGraphicsEndImageContext()

    return image

    }
}

Usage

var m_image = UIImage()

if let tempCaptureImg = self.m_Capture_View.viewCapture() {
    viewController.m_image = tempCaptureImg
}

// m_Capture_View is type of UIView

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPietro La SpadaView Question on Stackoverflow
Solution 1 - IosDavid RönnqvistView Answer on Stackoverflow
Solution 2 - IosImanou PetitView Answer on Stackoverflow
Solution 3 - IosVasily BodnarchukView Answer on Stackoverflow
Solution 4 - IosDaniel StormView Answer on Stackoverflow
Solution 5 - IosDamoView Answer on Stackoverflow
Solution 6 - IosneaveView Answer on Stackoverflow
Solution 7 - IosdiegomenView Answer on Stackoverflow
Solution 8 - IosnickcinView Answer on Stackoverflow
Solution 9 - IosGreggView Answer on Stackoverflow
Solution 10 - IosPung Worathiti ManosroiView Answer on Stackoverflow
Solution 11 - IosliquidView Answer on Stackoverflow
Solution 12 - IosJack ChenView Answer on Stackoverflow
Solution 13 - IosRaksha SainiView Answer on Stackoverflow
Solution 14 - IosRyanView Answer on Stackoverflow
Solution 15 - IosKamran GasimovView Answer on Stackoverflow
Solution 16 - IosAdam SmakaView Answer on Stackoverflow
Solution 17 - IosAlexander KhitevView Answer on Stackoverflow
Solution 18 - IosSaharat SittipanyaView Answer on Stackoverflow