ios 如何在 Swift 中进行全屏截图?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25448879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 02:00:25  来源:igfitidea点击:

How do I take a full screen Screenshot in Swift?

iosswiftuiimagescreenshot

提问by Pietro La Spada

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?

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

回答by David R?nnqvist

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 UIGraphicsBeginImageContextWithOptionsinstead. Then you can pass 0.0to get the same scale factor as the devices main screen.

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

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.

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

let image = UIGraphicsGetImageFromCurrentImageContext()

This line of code creates an UIImage object from what has been drawn into the graphics context.

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

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 viewand its subviews drawn into it.

结果是一个与 , 大小相同的图像view,其中view绘制了它的子视图。

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 UIWindowis a subclass of UIView, it can be drawn into a image context as well.

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

回答by Imanou Petit

Swift 4

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

为 Swift 2 更新

The code you provide works but doesn't allow you to capture the NavigationBarand the StatusBarin your screenshot. If you want to take a screenshot of your device that will include the NavigationBar, you have to use the following code:

您提供的代码有效,但不允许您在屏幕截图中捕获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)
}

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 StatusBarwill not appear in the final image.
  • 第一次启动应用程序并调用此方法时,iOS 设备会询问您是否允许将图像保存在相机胶卷中。
  • 此代码的结果将是一个 .JPG 图像。
  • StatusBar不会出现在最终图像。

回答by Vasily Bodnarchuk

Details

细节

  • Xcode 9.3, Swift 4.1
  • Xcode 10.2 (10E125) and 11.0 (11A420a), Swift 5
  • Xcode 9.3,斯威夫特 4.1
  • Xcode 10.2 (10E125) 和 11.0 (11A420a),Swift 5

Tested on iOS: 9, 10, 11, 12, 13

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

Solution

解决方案

import UIKit

extension UIApplication {

    func getKeyWindow() -> UIWindow? {
        if #available(iOS 13, *) {
            return windows.first { 
imageView.image = UIApplication.shared.makeSnapshot()

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

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

// or
imageView.image = UIImage(snapshotOf: view)
.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

用法

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


Old solution

旧解决方案

Xcode 8.2.1, swift 3

Xcode 8.2.1,快速 3

Version 1 for iOS 10x

iOS 10x 版本 1

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

Version 2 for iOS 9x, 10x

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

If you will try to use Version 1 code in iOS 9xyou will have error: CGImageCreateWithImageProvider: invalid image provider: NULL.

如果您尝试在 iOS 9x 中使用版本 1 代码,则会出现错误:CGImageCreateWithImageProvider: invalid image provider: NULL。

let screenShot = UIApplication.shared.screenShot!

Usage

用法

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

}

回答by Daniel Storm

Swift UIImageextension:

快速UIImage扩展:

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

Usage:

用法:

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

回答by Damo

For ease I would add an extension in it's own file

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

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

let windowImage = window.capture()

call the extension as follows...

拨打分机如下...

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

Similarly, one could extended UIView to capture an image of that....

类似地,可以扩展 UIView 以捕获该图像的图像....

回答by neave

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

在 iOS 10 中创建上下文的推荐方法是使用UIGraphicsImageRenderer.

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

回答by diegomen

I use this method:

我用这个方法:

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

  }
}]

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

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

Hope it helps!

希望能帮助到你!

回答by Gregg

Swift 3 Extension for UIWindow

UIWindow 的 Swift 3 扩展

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

回答by Pung Worathiti Manosroi

This is how I do it in Swift 4

这就是我在 Swift 4 中的做法

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
}

now screenshot will be type UIImage

现在截图将是类型 UIImage

回答by nickcin

Works with Swift 5and iOS 13

适用于Swift 5iOS 13

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

对于任何正在寻找函数的快速答案以将视图的屏幕截图作为 UIImage返回的人:

##代码##

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.

我根据问题中的代码并按照 David R?nnqvist 的建议(感谢您的解释)拼凑了这个答案,并进行了一些调整。

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

要包含导航栏和其他附加功能,请在window而不是view 之外调用此方法。

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

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