【问题标题】:Capturing full screenshot with status bar in iOS programmatically以编程方式在iOS中使用状态栏捕获完整屏幕截图
【发布时间】:2012-01-20 11:03:11
【问题描述】:

我正在使用此代码截取屏幕截图并将其保存到相册中。

-(void)TakeScreenshotAndSaveToPhotoAlbum
{
   UIWindow *window = [UIApplication sharedApplication].keyWindow;

   if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
       UIGraphicsBeginImageContextWithOptions(window.bounds.size, NO, [UIScreen mainScreen].scale);
   else
       UIGraphicsBeginImageContext(window.bounds.size);

   [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
   UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext();

   UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}

但问题是每当保存截图时,我看到 iPhone 的状态栏没有被捕获。相反,底部会出现一个空白。如下图:

我做错了什么?

【问题讨论】:

    标签: objective-c ios uiwindow uiscreen


    【解决方案1】:

    状态栏实际上是在它自己的 UIWindow 中,在你的代码中你只是渲染你的 viewcontroller 的视图,它不包括这个。

    “官方”截屏方法是 here,但现在似乎已被 Apple 删除,可能是因为它已经过时了。

    在 iOS 7 下,UIScreen 现在有一个新方法,用于获取包含整个屏幕内容的视图:

    - (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
    

    这将为您提供一个视图,然后您可以在屏幕上操作该视图以获得各种视觉效果。

    如果要将视图层次结构绘制到上下文中,则需要遍历应用程序的窗口(@98​​7654324@)并在每个窗口上调用此方法:

    - (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
    

    也许可以结合以上两种方法并拍摄快照视图,然后在快照上使用上述方法来绘制它。

    【讨论】:

    • 链接好像坏了。
    • 如果结合这两种方式,你会得到一个黑色的图像,显​​然快照视图不能这样绘制。即使您遍历屏幕上的窗口,也无法捕获状态栏。很遗憾。
    • 所以我猜正确的答案是绝对没有办法获取状态栏的屏幕截图?
    【解决方案2】:

    建议的“官方”截图方法不捕获状态栏(它不在应用程序的窗口列表中)。在 iOS 5 上测试过。

    我相信,这是出于安全原因,但文档中没有提及。

    我建议两种选择:

    • 从应用的资源中绘制存根状态栏图像(可选地更新时间指示器);
    • 仅捕获您的视图,没有状态栏,或随后修剪图像(图像大小将与标准设备分辨率不同);状态栏框架由应用对象的相应属性可知。

    【讨论】:

      【解决方案3】:

      这是我截取屏幕截图并将其存储为 NSData(在 IBAction 中)的代码。使用 sorted NSData 然后你可以分享或发送电子邮件或任何想做的事情

      CGSize imageSize = [[UIScreen mainScreen] bounds].size;
              if (NULL != UIGraphicsBeginImageContextWithOptions)
                  UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
              else
                  UIGraphicsBeginImageContext(imageSize);
      
              CGContextRef context = UIGraphicsGetCurrentContext();
      
              // Iterate over every window from back to front
              for (UIWindow *window in [[UIApplication sharedApplication] windows])
              {
                  if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen])
                  {
                      // -renderInContext: renders in the coordinate space of the layer,
                      // so we must first apply the layer's geometry to the graphics context
                      CGContextSaveGState(context);
                      // Center the context around the window's anchor point
                      CGContextTranslateCTM(context, [window center].x, [window center].y);
                      // Apply the window's transform about the anchor point
                      CGContextConcatCTM(context, [window transform]);
                      // Offset by the portion of the bounds left of and above the anchor point
                      CGContextTranslateCTM(context,
                                            -[window bounds].size.width * [[window layer] anchorPoint].x,
                                            -[window bounds].size.height * [[window layer] anchorPoint].y);
      
                      // Render the layer hierarchy to the current context
                      [[window layer] renderInContext:context];
      
                      // Restore the context
                      CGContextRestoreGState(context);
                  }
              }
      
              // Retrieve the screenshot image
              UIImage *imageForEmail = UIGraphicsGetImageFromCurrentImageContext();
      
              UIGraphicsEndImageContext();
      
          NSData *imageDataForEmail = UIImageJPEGRepresentation(imageForEmail, 1.0);
      

      【讨论】:

      • 当我使用键盘拍摄全屏屏幕截图时,它对我来说很好用..
      • 这些截图中没有状态栏
      • 尽管有人指控抄袭,但很高兴在这里有答案,在上下文中,而不仅仅是我的或将来可能无法使用的链接。
      【解决方案4】:

      Objective-C上面问题的答案已经写在那里,这里是上面问题的Swift版本答案。

      适用于 Swift 3+

      截取屏幕截图,然后将其显示在某处或通过网络发送。

      extension UIImage {
          class var screenShot: UIImage? {
              let imageSize = UIScreen.main.bounds.size as CGSize;
              UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
              guard let context = UIGraphicsGetCurrentContext() else {return nil}
              for obj : AnyObject in UIApplication.shared.windows {
                  if let window = obj as? UIWindow {
                      if window.responds(to: #selector(getter: UIWindow.screen)) || window.screen == UIScreen.main {
                          // so we must first apply the layer's geometry to the graphics context
                          context.saveGState();
                          // Center the context around the window's anchor point
                          context.translateBy(x: window.center.x, y: window.center
                              .y);
                          // Apply the window's transform about the anchor point
                          context.concatenate(window.transform);
                          // Offset by the portion of the bounds left of and above the anchor point
                          context.translateBy(x: -window.bounds.size.width * window.layer.anchorPoint.x,
                                               y: -window.bounds.size.height * window.layer.anchorPoint.y);
      
                          // Render the layer hierarchy to the current context
                          window.layer.render(in: context)
      
                          // Restore the context
                          context.restoreGState();
                      }
                  }
              }
              guard let image = UIGraphicsGetImageFromCurrentImageContext() else {return nil}
              return image
          }
      }
      

      以上截图的用法

      • 让我们在 UIImageView 上显示屏幕截图

        yourImageView = UIImage.screenShot
        
      • 获取图像数据以通过网络保存/发送

        if let img = UIImage.screenShot {
            if let data = UIImagePNGRepresentation(img) {
                //send this data over web or store it anywhere
            }
        }
        

      【讨论】:

      • 注意:以上解决方案要在获取图片后加上UIGraphicsEndImageContext(),以防泄露。
      【解决方案5】:

      斯威夫特,iOS 13:

      下面的代码(和其他访问方式)现在将导致应用程序崩溃并显示一条消息:

      在 UIApplication 上调用 -statusBar 或 -statusBarWindow 的应用程序:必须更改此代码,因为不再有状态栏或状态栏窗口。改为在窗口场景中使用 statusBarManager 对象。

      窗口场景和 statusBarManager 真的只允许我们访问框架 - 如果这仍然可能,我不知道如何。

      斯威夫特,iOS10-12:

      以下方法对我有用,并且在分析了用于捕获程序化屏幕截图的所有方法之后 - 这是 Apple 在 iOS 10 之后最快且推荐的方法

      let screenshotSize = CGSize(width: UIScreen.main.bounds.width * 0.6, height: UIScreen.main.bounds.height * 0.6)
      let renderer = UIGraphicsImageRenderer(size: screenshotSize)
      let statusBar = UIApplication.shared.value(forKey: "statusBarWindow") as? UIWindow
      let screenshot = renderer.image { _ in
          UIApplication.shared.keyWindow?.drawHierarchy(in: CGRect(origin: .zero, size: screenshotSize), afterScreenUpdates: true)
          statusBar?.drawHierarchy(in: CGRect(origin: .zero, size: screenshotSize), afterScreenUpdates: true)
      }
      

      您不必缩小屏幕截图大小(如果需要,可以直接使用UIScreen.main.bounds

      【讨论】:

        【解决方案6】:

        截取iPhone全屏,使用KVC获取状态栏:

        if let snapView = window.snapshotView(afterScreenUpdates: false) {
            if let statusBarSnapView = (UIApplication.shared.value(forKey: "statusBar") as? UIView)?.snapshotView(afterScreenUpdates: false) {
                snapView.addSubview(statusBarSnapView)
            }
            UIGraphicsBeginImageContextWithOptions(snapView.bounds.size, true, 0)
            snapView.drawHierarchy(in: snapView.bounds, afterScreenUpdates: true)
            let snapImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
        }
        

        【讨论】:

          【解决方案7】:

          以下对我有用,可以很好地捕获状态栏(iOS 9,Swift)

          let screen = UIScreen.mainScreen()
          let snapshotView = screen.snapshotViewAfterScreenUpdates(true)
          UIGraphicsBeginImageContextWithOptions(snapshotView.bounds.size, true, 0)
          snapshotView.drawViewHierarchyInRect(snapshotView.bounds, afterScreenUpdates: true)
          let image = UIGraphicsGetImageFromCurrentImageContext()
          UIGraphicsEndImageContext()
          

          【讨论】:

          • 这在模拟器中运行良好,但在 iOS 设备上似乎无法运行。我得到一个黑色矩形,即使我将它强制到主线程。
          • @snakeoil 你找到解决方案了吗
          • 非常适合模拟器,这解决了我自动生成 App Store 屏幕截图的问题,而无需迁移到 XCUITest 并通过启动参数注入所有上下文!
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-05
          • 2023-03-19
          • 1970-01-01
          • 1970-01-01
          • 2016-03-18
          相关资源
          最近更新 更多