在Swift iOS中如何截屏?

27

如何使用Swift编写代码从屏幕截取屏幕并保存?

我看到了这段代码,但似乎无法使用(我想)因为它什么也没有做。

var screen = UIScreen.mainScreen()
snapshotVieww = screen.snapshotViewAfterScreenUpdates(false)
UIGraphicsBeginImageContextWithOptions(screen.bounds.size, false, 0)
snapshotVieww.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
provino = UIImageView(image: image)

你在哪里/何时调用那段代码? - Daij-Djan
在“@IBAction func screen (sender: UIButton)”上。 - giorgionocera
尝试绘制snapshotVieww ...?可以描述一下snapshotVieww的信息吗? - Kumar KL
你是如何/何时展示新创建的ImageView的? - Daij-Djan
4个回答

44

在Swift 4 / iOS 10.3中,您可以选择以下其中一种方法来解决您的问题。


1. 对视图控制器的视图进行截图

以下代码显示了如何获取屏幕截图并将其保存在设备的照片相册中:

import UIKit

class ViewController: UIViewController {

    /* ... */

    @IBAction func screenshot(_ sender: UIBarButtonItem) {
        //Create the UIImage
        UIGraphicsBeginImageContextWithOptions(view.frame.size, true, 0)
        guard let context = UIGraphicsGetCurrentContext() else { return }
        view.layer.render(in: context)
        guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return }
        UIGraphicsEndImageContext()
        
        //Save it to the camera roll
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }

}

请注意,此代码的结果将是一个 .JPG 图像。另外,请注意最终图像中不会出现导航栏和状态栏。

自 iOS 10 开始,作为先前代码的替代方案,您可以使用下面的代码:

import UIKit

class ViewController: UIViewController {

    /* ... */

    @IBAction func screenshot(_ sender: UIBarButtonItem) {
        //Create the UIImage
        let renderer = UIGraphicsImageRenderer(size: view.frame.size)
        let image = renderer.image(actions: { context in
            view.layer.render(in: context.cgContext)
        })
        
        //Save it to the camera roll
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }

}

2. 拍摄 iPhone 窗口截图

如果您想拍摄包含导航栏(但不包括状态栏)的截图,您可以使用以下代码:

import UIKit

class ViewController: UIViewController {

    /* ... */

    @IBAction func screenshot(_ sender: UIBarButtonItem) {
        //Create the UIImage
        guard let layer = UIApplication.shared.keyWindow?.layer else { return }
        UIGraphicsBeginImageContextWithOptions(layer.frame.size, true, 0)
        guard let context = UIGraphicsGetCurrentContext() else { return }
        layer.render(in: context)
        guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return }
        UIGraphicsEndImageContext()
        
        //Save it to the camera roll
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
 
}

iOS 10以后,作为之前代码的替代方案,您可以使用以下代码:

import UIKit

class ViewController: UIViewController {

    /* ... */

    @IBAction func screenshot(_ sender: UIBarButtonItem) {
        //Create the UIImage
        guard let layer = UIApplication.shared.keyWindow?.layer else { return }
        let renderer = UIGraphicsImageRenderer(size: layer.frame.size)
        let image = renderer.image(actions: { context in
            layer.render(in: context.cgContext)
        })
        
        //Save it to the camera roll
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
     
}

提醒

自iOS 10以来,为了防止在调用screenshot(_:)方法时导致应用程序崩溃,您需要将键NSPhotoLibraryUsageDescription添加到项目的Info.plist文件中:

<key>NSPhotoLibraryUsageDescription</key>
<string>Some description to explain why access is required</string>

有没有办法提高拍摄的图像质量? - Nicholas
2
@Nicholas这段代码没有考虑比例因素,因此在Retina显示屏上会创建像素化的图像。 请使用以下代码替换:UIGraphicsBeginImageContextWithOptions(view.frame.size, false, UIScreen.mainScreen().scale) - Nick Kanellopoulos
如果您想保存到相册中,还需要“隐私-照片库添加使用说明”。 - Awais Fayyaz
@ThihaAung,你有找到在截图中包含状态栏的方法吗? - Anushk
有人知道如何解决这个问题吗?只在iOS13上出现了问题。 https://dev59.com/Grbna4cB1Zd3GeqPje1N - Aslam

9

只需这几行代码即可获取视图的截屏:(已测试)

  UIGraphicsBeginImageContextWithOptions(UIScreen.mainScreen().bounds.size, false, 0);
  self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
  var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();

  UIGraphicsEndImageContext();

  self.imgView.image = image;

你对iOS13中的这个问题有什么想法吗?https://dev59.com/Grbna4cB1Zd3GeqPje1N - Aslam
@IosDeveloper 我在iOS 13中遇到了类似的问题。使用此方法或类似方法(例如[NSString drawInRect: options:])渲染的任何文本都不可见。 - humblePilgrim
@humblePilgrim,我已经更新了可用的代码,你可以在下面的链接中查看 https://dev59.com/Grbna4cB1Zd3GeqPje1N - Aslam

2

//在 Facebook 或 Twitter 上分享截图就像这样...

func shareButtonClickedToTwitter(){
UIGraphicsBeginImageContextWithOptions(CGSizeMake(320,320), false, 0)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
self.view?.drawViewHierarchyInRect(CGRectMake(-30, -30, self.frame.size.width, self.frame.size.height), afterScreenUpdates: true)
var screenShot  = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.vc.showTWShare("I scored \(score) in Beanystalk can you do better? Available in App Store..", shareImage: screenShot)}


func shareButtonClickedToFaceboook() {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(320,320), false, 0)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
self.view?.drawViewHierarchyInRect(CGRectMake(-30, -30, self.frame.size.width, self.frame.size.height), afterScreenUpdates: true)
var screenShot  = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.vc.showFbShare("I got \(score) Points whilst playing BeanyStalk! Can you beat me?..", shareImageF: screenShot)  }

// Facebook分享功能的函数。

    func showFbShare(messageFB: String , shareImageF: UIImage) {
  println(messageFB)
  if SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook){
    var fbSheet = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
    fbSheet.completionHandler = {
      result in
      switch result {
      case SLComposeViewControllerResult.Cancelled:
        break
      case SLComposeViewControllerResult.Done:
        break
      }
    }
    fbSheet.addImage(shareImageF)
    fbSheet.setInitialText("\(messageFB) Click here to fun https://hereIsLink/")
    println(messageFB)
    self.presentViewController(fbSheet, animated: false, completion: {
    })
  }
  else {
    var alert = UIAlertController(title: "Accounts", message: "Please login to a facebook account to share.", preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
    self.presentViewController(alert, animated: true, completion: nil)
  }
}

// Twitter分享功能

func showTWShare(message: String , shareImage: UIImage) {
println(message)
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter){
  var twSheet = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
  twSheet.completionHandler = {
    result in
    switch result {
    case SLComposeViewControllerResult.Cancelled:
      break
    case SLComposeViewControllerResult.Done:
      break
    }
  }
  twSheet.setInitialText("\(message) Click here 2 fun https://hereIsLink/") //The default text in the tweet
  twSheet.addImage(shareImage)
  println(message)
  self.presentViewController(twSheet, animated: false, completion: {
  })
}
else {
  var alert = UIAlertController(title: "Accounts", message: "Please login to a Twitter account to share.", preferredStyle: UIAlertControllerStyle.Alert)
  alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
  self.presentViewController(alert, animated: true, completion: nil)
} }

1

要截屏,Imanou Petit的回答非常好。我已经处理了单个扩展中的多个iOS和tvOS版本,并将其作为pod(pod 'SwiftImageEffects')提供。

下面是完整代码(来自https://github.com/Coeur/ImageEffects/blob/master/SwiftImageEffects/ImageEffects%2Bextensions.swift):

extension UIView {
    /// Get a UIImage from the UIView
    /// - parameter opaque:
    /// A Boolean flag indicating whether the image is opaque. Specify true to ignore the alpha channel. Specify false to handle any partially transparent pixels.
    /// - parameter scale:
    /// The scale factor to apply to the image. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
    open func renderImage(opaque: Bool = false, scale: CGFloat = 0) -> UIImage {
        if #available(iOS 10.0, tvOS 10.0, *) {
            let format = UIGraphicsImageRendererFormat.default()
            format.opaque = opaque
            format.scale = scale
            return UIGraphicsImageRenderer(size: bounds.size, format: format).image { layer.render(in: $0.cgContext) }
        } else {
            // Fallback on earlier versions
            // The following methods will only return a 8-bit per channel context in the DeviceRGB color space.
            // Any new bitmap drawing code is encouraged to use UIGraphicsImageRenderer in lieu of this API.
            UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, scale)
            defer { UIGraphicsEndImageContext() }
            layer.render(in: UIGraphicsGetCurrentContext()!)
            return UIGraphicsGetImageFromCurrentImageContext()!
        }
    }
}

然后你可以简单地截取屏幕截图,如下所示:

func screenshot() {
    if let image = UIApplication.shared.keyWindow?.renderImage() {
        // saving will require a NSPhotoLibraryUsageDescription in your project's Info.plist
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
}

这很酷。但是,如果屏幕上有任何弹出窗口或模态框,当我使用此代码时,这些模态框周围会出现奇怪的厚阴影,是否有什么可以解决这个问题的方法? - zaitsman
@zaitsman 我不知道你的视图层次结构是怎样的。尝试在不同的窗口或不同的视图中调用 renderImage()。如果你无法解决问题,那么可以在 Stack Overflow 上提出具体的问题来寻求帮助。 - Cœur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接