如何使用 Swift 拍摄全屏截图?

我找到了 这个密码:

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

我需要做什么来获得所有其他元素,如导航栏,进入截图?

87979 次浏览

让我来解释一下您当前的代码是做什么的,以及如何修改它来捕捉全屏幕,而不仅仅是抛出答案。

UIGraphicsBeginImageContext(view.frame.size)

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

view.layer.renderInContext(UIGraphicsGetCurrentContext())

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

let image = UIGraphicsGetImageFromCurrentImageContext()

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

UIGraphicsEndImageContext()

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


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

如果你想把所有东西都画进图像,那么你应该创建一个屏幕大小的图像,并把屏幕上的所有东西都画进去。实际上,您可能只是在应用程序的“键窗口”中谈论所有内容。因为 UIWindowUIView的一个子类,所以它也可以被绘制到图像上下文中。

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
}

更新为 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不会出现在最终图像中。

我使用这种方法:

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

除了状态栏之外,它可以捕捉所有的图像,并且不需要在相机滚动中保存图像的许可。

希望能有帮助!

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

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 来捕获这个... 的图像。

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

快照视图(在屏幕更新之后: true)

快速 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)

Swift 3 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


}
}]

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

self.view.image() //returns UIImage

这是 Swift 3的解决方案

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

细节

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

IOS9x,10x 版本2

如果您尝试使用 IOS9x 中的版本1代码,那么将会出现错误: CGImageCreateWithImageProvider: 无效的图像提供程序: 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!

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

我的版本还捕捉了键盘。 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
}


}

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 的类型

我在 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

Swift 5IOS13一起工作

对于那些想要快速找到返回 风景截图的 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
}

通过采用问题中的代码和遵循 DavidRönnqvist 的建议(谢谢您的解释) ,我拼凑出了这个答案,并做了一些调整。

要包含导航条和其他附加内容,请从 窗户而不是 风景调用此方法。

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

此代码是最新版本-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!;
}

Swift 5

如果你只需要屏幕的真实快照(带键盘和状态栏) :

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

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

view.addSubview(snap)

场景更新

IOS 应用程序现在采用的场景,使上述解决方案无效。您必须对活动场景的主窗口进行快照,而不是对主屏幕进行快照。

如果您的应用程序只有一个场景,以下将工作。但是,如果您有多个场景,那么您必须首先找到活动场景(然后是它的主窗口)。

if let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate,
let snap = sceneDelegate.window?.snapshotView(afterScreenUpdates: false) {
view.addSubview(snap)
}

对于具有多个场景的应用程序: https://stackoverflow.com/a/69780721/9086770

Swift 5

捕获整个屏幕(减去状态栏) 而不会在新设备上崩溃(比如 iPhone12Pro)

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)的解决方案会导致一些设备(如 iPhone12Pro)的内存崩溃。