Screenshot in Swift iOS?

23,393

Solution 1

With Swift 4 / iOS 10.3, you can choose one of the following ways in order to solve your problem.


1. Take a screenshot of a view controller's view

The following code shows how to take a screenshot and save it in the device photo album:

import UIKit

class ViewController: UIViewController {

    /* ... */

    @IBAction func screenshot(_ sender: UIBarButtonItem) {
        //Create the UIImage
        UIGraphicsBeginImageContextWithOptions(view.frame.size, true, 0)
        guard let context = UIGraphicsGetCurrentContext() else { return }
        view.layer.render(in: context)
        guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return }
        UIGraphicsEndImageContext()
        
        //Save it to the camera roll
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }

}

Note that the result of this code will be a .JPG image. Also note that the navigation bar and the status bar will not appear in the final image.

Since iOS 10, as an alternative to the previous code, you can use the code below:

import UIKit

class ViewController: UIViewController {

    /* ... */

    @IBAction func screenshot(_ sender: UIBarButtonItem) {
        //Create the UIImage
        let renderer = UIGraphicsImageRenderer(size: view.frame.size)
        let image = renderer.image(actions: { context in
            view.layer.render(in: context.cgContext)
        })
        
        //Save it to the camera roll
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }

}

2. Take a screenshot of an iPhone window

If you want to take a screenshot that includes the navigation bar (but not the status bar), you can use the following code:

import UIKit

class ViewController: UIViewController {

    /* ... */

    @IBAction func screenshot(_ sender: UIBarButtonItem) {
        //Create the UIImage
        guard let layer = UIApplication.shared.keyWindow?.layer else { return }
        UIGraphicsBeginImageContextWithOptions(layer.frame.size, true, 0)
        guard let context = UIGraphicsGetCurrentContext() else { return }
        layer.render(in: context)
        guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return }
        UIGraphicsEndImageContext()
        
        //Save it to the camera roll
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
 
}

Since iOS 10, as an alternative to the previous code, you can use the code below:

import UIKit

class ViewController: UIViewController {

    /* ... */

    @IBAction func screenshot(_ sender: UIBarButtonItem) {
        //Create the UIImage
        guard let layer = UIApplication.shared.keyWindow?.layer else { return }
        let renderer = UIGraphicsImageRenderer(size: layer.frame.size)
        let image = renderer.image(actions: { context in
            layer.render(in: context.cgContext)
        })
        
        //Save it to the camera roll
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
     
}

Reminder

Since iOS 10, in order to prevent your app from crashing when calling your screenshot(_:) method, you need to add the key NSPhotoLibraryUsageDescription to your project's Info.plist file:

<key>NSPhotoLibraryUsageDescription</key>
<string>Some description to explain why access is required</string>

Solution 2

Just these few lines of code will get the screenShot of the View :(just Tested)

  UIGraphicsBeginImageContextWithOptions(UIScreen.mainScreen().bounds.size, false, 0);
  self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
  var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();

  UIGraphicsEndImageContext();

  self.imgView.image = image;

Solution 3

// Screen shot to share on face book or twitter is like that...

func shareButtonClickedToTwitter(){
UIGraphicsBeginImageContextWithOptions(CGSizeMake(320,320), false, 0)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
self.view?.drawViewHierarchyInRect(CGRectMake(-30, -30, self.frame.size.width, self.frame.size.height), afterScreenUpdates: true)
var screenShot  = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.vc.showTWShare("I scored \(score) in Beanystalk can you do better? Available in App Store..", shareImage: screenShot)}


func shareButtonClickedToFaceboook() {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(320,320), false, 0)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
self.view?.drawViewHierarchyInRect(CGRectMake(-30, -30, self.frame.size.width, self.frame.size.height), afterScreenUpdates: true)
var screenShot  = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.vc.showFbShare("I got \(score) Points whilst playing BeanyStalk! Can you beat me?..", shareImageF: screenShot)  }

// function for facebook share.

    func showFbShare(messageFB: String , shareImageF: UIImage) {
  println(messageFB)
  if SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook){
    var fbSheet = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
    fbSheet.completionHandler = {
      result in
      switch result {
      case SLComposeViewControllerResult.Cancelled:
        break
      case SLComposeViewControllerResult.Done:
        break
      }
    }
    fbSheet.addImage(shareImageF)
    fbSheet.setInitialText("\(messageFB) Click here to fun https://hereIsLink/")
    println(messageFB)
    self.presentViewController(fbSheet, animated: false, completion: {
    })
  }
  else {
    var alert = UIAlertController(title: "Accounts", message: "Please login to a facebook account to share.", preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
    self.presentViewController(alert, animated: true, completion: nil)
  }
}

// Twitter Share function

func showTWShare(message: String , shareImage: UIImage) {
println(message)
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter){
  var twSheet = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
  twSheet.completionHandler = {
    result in
    switch result {
    case SLComposeViewControllerResult.Cancelled:
      break
    case SLComposeViewControllerResult.Done:
      break
    }
  }
  twSheet.setInitialText("\(message) Click here 2 fun https://hereIsLink/") //The default text in the tweet
  twSheet.addImage(shareImage)
  println(message)
  self.presentViewController(twSheet, animated: false, completion: {
  })
}
else {
  var alert = UIAlertController(title: "Accounts", message: "Please login to a Twitter account to share.", preferredStyle: UIAlertControllerStyle.Alert)
  alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
  self.presentViewController(alert, animated: true, completion: nil)
} }

Solution 4

To take a screenshot, Imanou Petit answer is very good. I've dealt with the multiple versions of iOS and tvOS in a single extension, and made it available as a pod (pod 'SwiftImageEffects').

Full code below (from https://github.com/Coeur/ImageEffects/blob/master/SwiftImageEffects/ImageEffects%2Bextensions.swift):

extension UIView {
    /// Get a UIImage from the UIView
    /// - parameter opaque:
    /// A Boolean flag indicating whether the image is opaque. Specify true to ignore the alpha channel. Specify false to handle any partially transparent pixels.
    /// - parameter scale:
    /// The scale factor to apply to the image. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
    open func renderImage(opaque: Bool = false, scale: CGFloat = 0) -> UIImage {
        if #available(iOS 10.0, tvOS 10.0, *) {
            let format = UIGraphicsImageRendererFormat.default()
            format.opaque = opaque
            format.scale = scale
            return UIGraphicsImageRenderer(size: bounds.size, format: format).image { layer.render(in: $0.cgContext) }
        } else {
            // Fallback on earlier versions
            // The following methods will only return a 8-bit per channel context in the DeviceRGB color space.
            // Any new bitmap drawing code is encouraged to use UIGraphicsImageRenderer in lieu of this API.
            UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, scale)
            defer { UIGraphicsEndImageContext() }
            layer.render(in: UIGraphicsGetCurrentContext()!)
            return UIGraphicsGetImageFromCurrentImageContext()!
        }
    }
}

Then you may take a screenshot as simply as:

func screenshot() {
    if let image = UIApplication.shared.keyWindow?.renderImage() {
        // saving will require a NSPhotoLibraryUsageDescription in your project's Info.plist
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
}
Share:
23,393

Related videos on Youtube

giorgio.nocera
Author by

giorgio.nocera

Updated on July 09, 2022

Comments

  • giorgio.nocera
    giorgio.nocera almost 2 years

    How can I take screenshot from my screen by code (in Swift) and save it? Can I take screenshot and save it as image?

    I've looked and I see this code, but I can't use it (I think) because it doesn't do anything.

    var screen = UIScreen.mainScreen()
    snapshotVieww = screen.snapshotViewAfterScreenUpdates(false)
    UIGraphicsBeginImageContextWithOptions(screen.bounds.size, false, 0)
    snapshotVieww.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
    var image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    provino = UIImageView(image: image)
    
    • Daij-Djan
      Daij-Djan over 9 years
      where/when do you call that code
    • giorgio.nocera
      giorgio.nocera over 9 years
      On a " @IBAction func screen (sender: UIButton) "
    • Kumar KL
      Kumar KL over 9 years
      trying to draw snapshotVieww ...? N describe about snapshotVieww?
    • Daij-Djan
      Daij-Djan over 9 years
      And how/when do you show the newly made imageview?
  • Nicholas
    Nicholas over 8 years
    Is there a way to improve the quality of the image taken?
  • Nick Kanellopoulos
    Nick Kanellopoulos almost 8 years
    @Nicholas This code does not take into account the scale factor, so it creates pixelated images in Retina displays. Replace with this: UIGraphicsBeginImageContextWithOptions(view.frame.size, false, UIScreen.mainScreen().scale)
  • Awais Fayyaz
    Awais Fayyaz over 5 years
    you also need Privacy - Photo Library Additions Usage Description if you want to save to photo albums
  • Anushk
    Anushk over 5 years
    @ThihaAung did you ever find a way to include the status bar in screenshot?
  • Aslam
    Aslam over 4 years
    Anyone have idea how to solve this issue..facing issue in only iOS13 . stackoverflow.com/questions/58405106/…
  • Aslam
    Aslam over 4 years
    did you have any idea about this issue in iOS13 stackoverflow.com/questions/58405106/…
  • humblePilgrim
    humblePilgrim over 4 years
    @IosDeveloper I am facing issues in similar scenarios in iOS 13. No text rendered using this or similar methods such as [NSString drawInRect: options:] is visible
  • Aslam
    Aslam over 4 years
    @humblePilgrim i have updated working code you can check below link stackoverflow.com/questions/58405106/…
  • zaitsman
    zaitsman about 4 years
    This is cool. But if i have any popovers or modals presented in the screen, when i use this code i get weird thick shadows around those modals, is there something that can be done about that?
  • Cœur
    Cœur about 4 years
    @zaitsman I don't know how is your view hierarchy. Try to renderImage() a different window or a different view. If you can't solve it, then maybe ask a specific Stack Overflow question for those shadows.