Захват полного снимка экрана с помощью строки состояния в iOS программно - PullRequest
14 голосов
/ 14 декабря 2011

Я использую этот код, чтобы сделать снимок экрана и сохранить его в фотоальбоме.

-(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 не отображается. Вместо этого пробел появляется внизу. Как следующее изображение: enter image description here

Что я делаю не так?

Ответы [ 6 ]

18 голосов
/ 14 декабря 2011

Строка состояния на самом деле находится в собственном UIWindow, в вашем коде вы отображаете только представление вашего viewcontroller, которое не включает это.

"Официальный" метод скриншота был здесь , но теперь, похоже, Apple его удалил, возможно, из-за того, что он устарел.

В iOS 7 теперь есть новый метод UIScreen для получения представления, содержащего содержимое всего экрана:

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

Это даст вам представление, которым вы можете затем манипулировать на экране для различных визуальных эффектов.

Если вы хотите нарисовать иерархию представления в контексте, вам нужно перебрать окна приложения ([[UIApplication sharedApplication] windows]) и вызвать этот метод для каждого из них:

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

Вы можете быть в состоянии объединить два вышеупомянутых подхода и сделать снимок экрана, а затем использовать описанный выше метод на снимке, чтобы нарисовать его.

7 голосов
/ 26 декабря 2011

Предлагаемый «официальный» метод скриншотов не захватывает строку состояния (ее нет в списке окон приложения). Как проверено на iOS 5.

Полагаю, это по соображениям безопасности, но в документах об этом нет упоминания.

Я предлагаю два варианта:

  • нарисовать заглушку в строке состояния из ресурсов вашего приложения (опционально обновить индикатор времени);
  • захватывать только ваш вид без строки состояния или обрезки изображения впоследствии (размер изображения будет отличаться от стандартного разрешения устройства); рамка строки состояния известна из соответствующего свойства объекта приложения.
6 голосов
/ 03 марта 2013

Вот мой код, чтобы сделать снимок экрана и сохранить его как 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 голос
/ 24 марта 2018

Ответ на вышеуказанный вопрос для 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
        }
    }
    
0 голосов
/ 07 марта 2019

Захватите весь экран 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()
}
0 голосов
/ 05 марта 2016

Следующее работает для меня, отлично захватывает строку состояния (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()
...