如何在 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
及其子视图。
如果您想将所有内容都绘制到图像中,那么您应该创建一个与屏幕大小相同的图像,并将屏幕上的所有内容都绘制到其中。在实践中,您可能只是在谈论应用程序“关键窗口”中的所有内容。由于UIWindow
是UIView
的子类,因此也可以将其绘制到图像上下文中。
【讨论】:
当我尝试这个来捕捉屏幕图像时,结果是一个有点像素化的图像。我不知道为什么。你知道大卫吗? 请注意,我没有更改任何 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 更新
您提供的代码有效,但不允许您在屏幕截图中捕获NavigationBar
和StatusBar
。如果您想截取包含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 您需要将密钥详情
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 5 和 iOS 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 中截取全屏截图?的主要内容,如果未能解决你的问题,请参考以下文章