【问题标题】:iOS Take Screenshot on Curent Device Display not App ScreeniOS 在当前设备显示而不是应用屏幕上截屏
【发布时间】:2020-06-27 13:46:19
【问题描述】:

我有这些代码(用 C# 编写)来截屏,但它只截取 App Screen,即使我最小化我的应用程序,它仍然捕获我的 App 屏幕。我想占据整个设备屏幕,即使我打开另一个应用程序,它也会显示其他应用程序、桌面等的屏幕截图...

在 C# Xamarin 中

var view = UIApplication.SharedApplication.KeyWindow.RootViewController.View;

UIGraphics.BeginImageContext(view.Frame.Size);
view.DrawViewHierarchy(view.Frame, true);
var image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();

using (var imageData = image.AsPNG())
{
    var bytes = new byte[imageData.Length];
    System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, bytes, 0, Convert.ToInt32(imageData.Length));
    return bytes;
}

您也可以在 Swift/Objective-C 中发布建议。

【问题讨论】:

    标签: c# ios swift xamarin.ios


    【解决方案1】:

    在您的情况下,您应该使用 UIScreenView 而不是当前的 ViewController

    CGSize size = UIScreen.MainScreen.Bounds.Size;
    nfloat scale = UIScreen.MainScreen.Scale;
    
    UIGraphics.BeginImageContextWithOptions(size,true,scale);
               UIApplication.SharedApplication.KeyWindow.Layer.RenderInContext(UIGraphics.GetCurrentContext());
    
    UIImage image = UIGraphics.GetImageFromCurrentImageContext();
    
    UIGraphics.EndImageContext();
    

    如果你想让屏幕截图包含状态栏,你可以参考 以下 OC 代码

    
    + (UIImage *)screenshotWithStatusBar:(BOOL)withStatusBar rect:(CGRect)rect
    {
        UIInterfaceOrientation o = [[UIApplication sharedApplication] statusBarOrientation];
        return [self screenshotWithStatusBar:withStatusBar rect:rect orientation:o];
    }
    
    + (UIImage *)screenshotWithStatusBar:(BOOL)withStatusBar rect:(CGRect)rect orientation:(UIInterfaceOrientation)o
    {
        CGRect screenRect = [[UIScreen mainScreen] bounds];
        CGFloat screenWidth = CGRectGetWidth(screenRect);
        CGFloat screenHeight = CGRectGetHeight(screenRect);
        CGAffineTransform preTransform = CGAffineTransformIdentity;
        switch (o)
        {
            case UIInterfaceOrientationPortrait:
                //move screenshot rect origin to down left
                //rotate screenshot rect to meet portrait
                //move screenshot rect origin to up left
                //....yes, with a single line..
                preTransform = CGAffineTransformTranslate(preTransform, -rect.origin.x, -rect.origin.y);
                break;
            case UIInterfaceOrientationPortraitUpsideDown:
                //move screenshot rect origin to down left
                preTransform = CGAffineTransformTranslate(preTransform, screenWidth - rect.origin.x, -rect.origin.y);
                //rotate screenshot rect to meet portrait
                preTransform = CGAffineTransformRotate(preTransform, M_PI);
                //move screenshot rect origin to up left
                preTransform = CGAffineTransformTranslate(preTransform, 0, -screenHeight);
                break;
            case UIInterfaceOrientationLandscapeLeft:
                //move screenshot rect origin to down left
                preTransform = CGAffineTransformTranslate(preTransform, -rect.origin.x, -rect.origin.y);
                //rotate screenshot rect to meet portrait
                preTransform = CGAffineTransformRotate(preTransform, M_PI_2);
                //move screenshot rect origin to up left
                preTransform = CGAffineTransformTranslate(preTransform, 0, -screenHeight);
                break;
            case UIInterfaceOrientationLandscapeRight:
                //move screenshot rect origin to down left
                preTransform = CGAffineTransformTranslate(preTransform, screenHeight - rect.origin.x, screenWidth - rect.origin.y);
                //rotate screenshot rect to meet portrait
                preTransform = CGAffineTransformRotate(preTransform, - M_PI_2);
                //move screenshot rect origin to up left
                preTransform = CGAffineTransformTranslate(preTransform, 0, -screenHeight);
                break;
            default:
                break;
        }
    
        // Create a graphics context with the target size
        UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
    
        CGContextRef context = UIGraphicsGetCurrentContext();
    
        BOOL hasTakenStatusBarScreenshot = NO;
        // 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);
    
                // Apply pre tranform to context.
                // to convert all interface orientation situation to portrait situation.
                CGContextConcatCTM(context, preTransform);
                // 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
                if ([window respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
                    [window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
                } else {
                    [window.layer renderInContext:context];
                }
    
                // Restore the context
                CGContextRestoreGState(context);
            }
    
            // Screenshot status bar if next window's window level > status bar window level
            NSArray *windows = [[UIApplication sharedApplication] windows];
            NSUInteger currentWindowIndex = [windows indexOfObject:window];
            if (windows.count > currentWindowIndex + 1)
            {
                UIWindow *nextWindow = [windows objectAtIndex:currentWindowIndex + 1];
                if (withStatusBar && nextWindow.windowLevel > UIWindowLevelStatusBar && !hasTakenStatusBarScreenshot)
                {
                    [self mergeStatusBarToContext:context rect:rect screenshotOrientation:o];
                    hasTakenStatusBarScreenshot = YES;
                }
            }
            else
            {
                if (withStatusBar && !hasTakenStatusBarScreenshot)
                {
                    [self mergeStatusBarToContext:context rect:rect screenshotOrientation:o];
                    hasTakenStatusBarScreenshot = YES;
                }
            }
        }
    
        // Retrieve the screenshot image
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    
        UIGraphicsEndImageContext();
    
        return image;
    }
    
    + (void)mergeStatusBarToContext:(CGContextRef)context
                               rect:(CGRect)rect
              screenshotOrientation:(UIInterfaceOrientation)o
    {
        UIView *statusBarView = [UIView statusBarInstance_ComOpenThreadOTScreenshotHelper];
        UIInterfaceOrientation statusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation];
        CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
        CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
        CGAffineTransform preTransform = CGAffineTransformIdentity;
        if (o == statusBarOrientation)
        {
            preTransform = CGAffineTransformTranslate(preTransform, -rect.origin.x, -rect.origin.y);
        }
        //Handle status bar orientation in portrait and portrait upside down screen shot
        else if((o == UIInterfaceOrientationPortrait && statusBarOrientation == UIInterfaceOrientationLandscapeLeft) ||
                (o == UIInterfaceOrientationPortraitUpsideDown && statusBarOrientation == UIInterfaceOrientationLandscapeRight))
        {
            preTransform = CGAffineTransformTranslate(preTransform, 0, rect.size.height);
            preTransform = CGAffineTransformRotate(preTransform, - M_PI_2);
            preTransform = CGAffineTransformTranslate(preTransform, CGRectGetMaxY(rect) - screenHeight, -rect.origin.x);
        }
        else if((o == UIInterfaceOrientationPortrait && statusBarOrientation == UIInterfaceOrientationLandscapeRight) ||
                (o == UIInterfaceOrientationPortraitUpsideDown && statusBarOrientation == UIInterfaceOrientationLandscapeLeft))
        {
            preTransform = CGAffineTransformTranslate(preTransform, 0, rect.size.height);
            preTransform = CGAffineTransformRotate(preTransform, M_PI_2);
            preTransform = CGAffineTransformTranslate(preTransform, -CGRectGetMaxY(rect), rect.origin.x - screenWidth);
        }
        else if((o == UIInterfaceOrientationPortrait && statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown) ||
                (o == UIInterfaceOrientationPortraitUpsideDown && statusBarOrientation == UIInterfaceOrientationPortrait))
        {
            preTransform = CGAffineTransformTranslate(preTransform, 0, rect.size.height);
            preTransform = CGAffineTransformRotate(preTransform, - M_PI);
            preTransform = CGAffineTransformTranslate(preTransform, rect.origin.x - screenWidth, CGRectGetMaxY(rect) - screenHeight);
        }
        //Handle status bar orientation in landscape left and landscape right screen shot
        else if((o == UIInterfaceOrientationLandscapeLeft && statusBarOrientation == UIInterfaceOrientationPortrait) ||
                (o == UIInterfaceOrientationLandscapeRight && statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown))
        {
            preTransform = CGAffineTransformTranslate(preTransform, 0, rect.size.height);
            preTransform = CGAffineTransformRotate(preTransform, M_PI_2);
            preTransform = CGAffineTransformTranslate(preTransform, -CGRectGetMaxY(rect), rect.origin.x - screenHeight);
        }
        else if((o == UIInterfaceOrientationLandscapeLeft && statusBarOrientation == UIInterfaceOrientationLandscapeRight) ||
                (o == UIInterfaceOrientationLandscapeRight && statusBarOrientation == UIInterfaceOrientationLandscapeLeft))
        {
            preTransform = CGAffineTransformTranslate(preTransform, 0, rect.size.height);
            preTransform = CGAffineTransformRotate(preTransform, M_PI);
            preTransform = CGAffineTransformTranslate(preTransform, rect.origin.x - screenHeight, CGRectGetMaxY(rect) - screenWidth);
        }
        else if((o == UIInterfaceOrientationLandscapeLeft && statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown) ||
                (o == UIInterfaceOrientationLandscapeRight && statusBarOrientation == UIInterfaceOrientationPortrait))
        {
            preTransform = CGAffineTransformTranslate(preTransform, 0, rect.size.height);
            preTransform = CGAffineTransformRotate(preTransform, - M_PI_2);
            preTransform = CGAffineTransformTranslate(preTransform, CGRectGetMaxY(rect) - screenWidth, -rect.origin.x);
        }
    
        // -renderInContext: renders in the coordinate space of the layer,
        // so we must first apply the layer's geometry to the graphics context
        CGContextSaveGState(context);
        // Apply pre transform
        CGContextConcatCTM(context, preTransform);
        // Center the context around the window's anchor point
        CGContextTranslateCTM(context, [statusBarView center].x, [statusBarView center].y);
        // Apply the view transform about the anchor point
        CGContextConcatCTM(context, [statusBarView transform]);
        // Offset by the portion of the bounds left of and above the anchor point
        CGContextTranslateCTM(context,
                              -[statusBarView bounds].size.width * [[statusBarView layer] anchorPoint].x,
                              -[statusBarView bounds].size.height * [[statusBarView layer] anchorPoint].y);
    
        // Render the layer hierarchy to the current context
        if ([statusBarView respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
            [statusBarView drawViewHierarchyInRect:statusBarView.bounds afterScreenUpdates:YES];
        } else {
            [statusBarView.layer renderInContext:context];
        }
    
        // Restore the context
        CGContextRestoreGState(context);
    }
    

    【讨论】:

      【解决方案2】:

      不幸的是,这是不可能的。出于安全原因,iOS 不允许应用截取除它们自己的视图层次结构之外的任何内容。

      更新:澄清一下,在您的应用程序中获取其他应用程序屏幕截图的唯一方法是让用户手动截取屏幕截图(使用主页 + 电源按钮),然后让用户发送/分享到您的应用程序(supporting and handling image file types),或让用户从他们的照片库中选择它(使用UIImagePickerController)。

      目前没有选项可以完全自动化此过程。

      【讨论】:

      • 但是Apple Store 上有很多应用程序可以做到这一点。甚至 ReplayKit 也可以录制屏幕并将其流式传输。/
      • 你能说出这些应用的例子吗?甚至 ReplayKit 记录器和广播器也仅限于记录或广播它启动的应用程序的视图。请参阅苹果文档:developer.apple.com/documentation/replaykit/rpscreenrecorder“您的应用可以录制应用内部的音频和视频”。
      • 几年前的一次 WWDC 会议似乎表明应用可以使用 ReplayKit 2 的直播功能在其应用之外流式传输内容:developer.apple.com/videos/play/wwdc2017/606
      • @RyanPendleton 要使用 ReplayKit 录制整个 iOS 屏幕(包括所有应用程序),您必须从 iOS 控制面板开始录制/流式传输。您可以将视频/流数据发送到应用程序进行处理(使用广播扩展),但重新编码/流本身只能从 iOS 系统界面控制。在应用内启动和控制的录制/流只能记录其应用视图层次结构。
      • 截图也一样。您可以使用系统按钮(主页+电源)在任何地方截取任何应用程序的屏幕截图,然后将屏幕截图发送/共享到应用程序。但是应用程序无法启动或控制截屏(除非它是它们自己的视图层次结构)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      相关资源
      最近更新 更多