如何在 Swift 中截取全屏截图?

Posted

技术标签:

【中文标题】如何在 Swift 中截取全屏截图?【英文标题】:How do I take a full screen Screenshot in 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 谢谢,但没有状态栏。 【参考方案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 张屏幕截图。如何使用计时器截屏?

以上是关于如何在 Swift 中截取全屏截图?的主要内容,如果未能解决你的问题,请参考以下文章

电脑截全屏怎么截?

如何在 Android 上以编程方式截取屏幕截图? [复制]

截图技巧 一按截图的快捷键下拉菜单就消失 无法截图

Windows10自带截图

电脑怎么全屏截图

在C#中如何实现winform窗体的全屏截图功能