【问题标题】:How do I take a full screen Screenshot in Swift?如何在 Swift 中截取全屏截图?
【发布时间】:2014-08-22 14:03:19
【问题描述】:

我找到了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)
}

我需要做什么才能将所有其他元素(例如导航栏)放入屏幕截图中?

【问题讨论】:

  • 你知道如何在Objective C中全屏截图吗?
  • 是的,但我不知道相关代码。
  • CALayer *layer = [[UIApplication sharedApplication] keyWindow].layer; CGFloat scale = [UIScreen mainScreen].scale; UIGraphicsBeginImageContextWithOptions(layer.frame.size, NO, scale); [图层 renderInContext:UIGraphicsGetCurrentContext()];图像 = UIGraphicsGetImageFromCurrentImageContext();这是在目标 C 中拍摄全屏截图,并将其转换为 par swift
  • 谢谢,但没有状态栏。

标签: ios swift uiimage screenshot


【解决方案1】:

让我解释一下您当前的代码做了什么以及如何修改它以捕获全屏,而不是直接把答案扔在那里。

UIGraphicsBeginImageContext(view.frame.size)

这行代码创建了一个与view 大小相同的新图像上下文。这里要重点说明的是新图像上下文的大小view 相同。除非您想捕获应用程序的低分辨率(非视网膜)版本,否则您可能应该改用 UIGraphicsBeginImageContextWithOptions。然后您可以通过0.0 获得与设备主屏幕相同的比例因子。

view.layer.renderInContext(UIGraphicsGetCurrentContext())

这行代码会将视图层渲染到当前图形上下文(即您刚刚创建的上下文)中。这里主要需要注意的是,只有view(及其子视图)被绘制到图像上下文中。

let image = UIGraphicsGetImageFromCurrentImageContext()

这行代码根据已绘制到图形上下文中的内容创建一个 UIImage 对象。

UIGraphicsEndImageContext()

这行代码结束了图像上下文。它已清理(您创建了上下文,也应该将其删除。


结果是一个与view 大小相同的图像,其中包含view 及其子视图。

如果您想将所有内容都绘制到图像中,那么您应该创建一个与屏幕大小相同的图像,并将屏幕上的所有内容都绘制到其中。在实践中,您可能只是在谈论应用程序“关键窗口”中的所有内容。由于UIWindowUIView 的子类,因此也可以将其绘制到图像上下文中。

【讨论】:

  • 当我尝试这个来捕捉屏幕图像时,结果是一个有点像素化的图像。我不知道为什么。你知道大卫吗?
  • 请注意,我没有更改任何 OP 代码,只是朝着正确的方向推动。您看到的像素化外观来自图像上下文的低比例因子。如果您阅读UIGraphicsBeginImageContext 的文档,您会看到它以1.0 的比例因子调用UIGraphicsBeginImageContextWithOptions。阅读 its 文档会告诉您,您可以将比例因子设置为 0.0 以与您的主屏幕相同。
  • 谢谢大卫。确实将比例因子设置为 0 有效。
  • 我知道这是一个旧线程,但有没有办法捕捉屏幕上呈现的所有内容,包括警报?
  • @AlexanderKhitev 我认为这是不可能的,因为键盘不是您的应用程序的一部分。如果您能够截取整个屏幕的屏幕截图(例如,iPad 分屏有两个应用程序),那将是一场隐私噩梦。
【解决方案2】:

斯威夫特 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
    }

为 Swift 2 更新

您提供的代码有效,但不允许您在屏幕截图中捕获NavigationBarStatusBar。如果您想截取包含NavigationBar 的设备屏幕截图,您必须使用以下代码:

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)
}

使用此代码:

  • 首次启动应用并调用此方法时,iOS 设备会询问您是否允许将图像保存在相机胶卷中。
  • 此代码的结果将是一个 .JPG 图像。
  • StatusBar 不会出现在最终图像中。

【讨论】:

  • 好方法!由于比例尺,它以全视网膜分辨率捕获图像。非常感谢这段代码!颜-
  • 不幸的是,这根本不包括您应用于整个图像的任何层的任何 CATransform3D。
  • 在 iOS 10.x 下使用 swift 3.0 您需要将密钥 NSPhotoLibraryUsageDescriptionAdd to your photo library 添加到您的 Info.plist
  • 如何在截图中也包含状态栏?
  • 不是真的正确,因为如果类型是可选的,你不能返回 nil 你的行 "guard let context = UIGraphicsGetCurrentContext() else {return nil}" 不正确
【解决方案3】:

详情

  • Xcode 9.3、Swift 4.1
  • Xcode 10.2 (10E125) 和 11.0 (11A420a),Swift 5

在 iOS 上测试:9、10、11、12、13

解决方案

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)
    }
}

用法

imageView.image = UIApplication.shared.makeSnapshot()

// or
imageView.image = view.makeSnapshot()

// or
imageView.image = view.layer.makeSnapshot()

// or
imageView.image = UIImage(snapshotOf: view)

旧解决方案

Xcode 8.2.1,快速 3

iOS 10x 版本 1

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

适用于 iOS 9x、10x 的版本 2

如果您尝试在 iOS 9x 中使用 版本 1 代码,您将遇到错误: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
    }
}

用法

let screenShot = UIApplication.shared.screenShot!

【讨论】:

  • 如果我想捕获 AVPlayerViewController 的自定义外观和 UIView 怎么办?任何建议请!
  • 我试过这段代码,但它没有捕获键盘,对吧?
【解决方案4】:

Swift UIImage 扩展:

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)
    }

}

用法:

let myImage: UIImage? = UIImage(view: myView)

【讨论】:

  • 那是截取特定视图,而不是整个屏幕。
【解决方案5】:

为方便起见,我会在它自己的文件中添加扩展名

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

按如下方式调用扩展...

let window: UIWindow! = UIApplication.sharedApplication().keyWindow

let windowImage = window.capture()

同样,我们可以扩展 UIView 来捕捉它的图像....

【讨论】:

    【解决方案6】:

    在 iOS 10 中创建上下文的推荐方法是使用 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
        }
    }
    

    【讨论】:

    • 这工作得很好,但在可滚动视图(如集合视图)的情况下,生成的图像是第一个视图,而不是实际的当前视图。
    【解决方案7】:

    我用这个方法:

    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;
    }
    

    它会捕获除状态栏以外的所有内容,并且不会请求将图像保存在相机胶卷中的权限。

    希望对你有帮助!

    【讨论】:

      【解决方案8】:

      用于 UIWindow 的 Swift 3 扩展

      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
      
        }
      }]
      

      【讨论】:

        【解决方案9】:

        这就是我在 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()
        

        现在屏幕截图将输入 UIImage

        【讨论】:

          【解决方案10】:

          适用于 Swift 5iOS 13

          对于任何寻找函数的快速答案以将 view 的屏幕截图作为 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
          }
          

          我通过获取问题中的代码并遵循 David Rönnqvist 的建议(感谢您的解释)拼凑出这个答案,并进行了一些调整。

          要包含导航栏和其他附加功能,请在 窗口 而不是 视图 中调用此方法。

          我只需要一个函数来获取视图的屏幕截图,所以我希望这可以帮助任何寻找相同内容的人

          【讨论】:

            【解决方案11】:

            斯威夫特 5

            如果您只需要那一刻的真实屏幕快照(带有键盘和状态栏):

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

            snap 是一个UIView,其框架自动设置为屏幕边界。现在将它添加到视图控制器的视图中会给你一个看起来冻结的 UI:

            view.addSubview(snap)
            

            不需要扩展和所有不必要的东西——两行代码就足够了。

            【讨论】:

            • 这在你有一个显示 AVPlayer 的 WKWebView 的情况下不起作用,例如在滑动 mp4 视频时。
            【解决方案12】:
              // 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
              }
            

            【讨论】:

              【解决方案13】:

              这是相似的,希望它在未来对某人有所帮助。

              self.view.image() //returns UIImage
              

              这是一个 Swift 3 解决方案

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

              【讨论】:

                【解决方案14】:

                此代码是最新版本 - 100% 有效

                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!;
                }
                

                【讨论】:

                  【解决方案15】:

                  view.snapshotView(afterScreenUpdates: true)

                  【讨论】:

                    【解决方案16】:

                    我的版本还捕获了一个键盘。斯威夫特 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
                        }
                    
                    }
                    

                    【讨论】:

                    • 但是它没有捕获状态栏,有什么建议吗?
                    • @Pavan 我不知道,因为在我当前的应用程序中没有状态栏。
                    • 我在示例代码上进行了尝试,例如通过电源+主页按钮模仿屏幕截图,但此代码不捕获状态栏。
                    【解决方案17】:

                    Swift 4 或更高版本。

                    用于您捕获的 UIView 的扩展和调用。

                    声明

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

                    用法

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

                    // m_Capture_View 是 UIView 的类型

                    【讨论】:

                      【解决方案18】:

                      斯威夫特 5

                      捕获整个屏幕(减去状态栏)不会在较新的设备(如 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
                          }
                      }
                      

                      以前使用UIApplication.shared.keyWindow?.layer.render(in: context) 的解决方案会导致某些设备(如 iPhone 12 Pro)出现内存崩溃。

                      【讨论】:

                      • 我想截取 gif 添加的图像视图并保存。我想要实现的目标是使用计时器进行 gif 移动拍摄 50 张屏幕截图。如何使用计时器截屏?
                      猜你喜欢
                      • 1970-01-01
                      • 2017-10-04
                      • 1970-01-01
                      • 2011-08-16
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多