以编程方式在iOS中捕获状态栏的完整屏幕截图

问题描述 投票:14回答:6

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

-(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
6个回答
18
投票

状态栏实际上在它自己的UIWindow中,在您的代码中,您只渲染viewcontroller的视图,但不包含此视图。

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

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

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

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

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

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

您可以将上述两种方法结合起来并获取快照视图,然后在快照上使用上述方法绘制它。


7
投票

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

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

我建议两个选择:

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

6
投票

这是我的代码,用于截取屏幕截图并将其存储为NSData(在IBAction中)。使用sotred 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);

1
投票

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

1
投票

Swift, iOS10+:

以下对我有用,并且在描述了捕获程序化屏幕截图的所有方法之后 - 这是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


0
投票

捕获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()
}

-1
投票

以下适用于我,捕获状态栏正常(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()
© www.soinside.com 2019 - 2024. All rights reserved.