Como tirar uma captura de tela programaticamente no iOS

157

Quero uma captura de tela da imagem na tela salva na biblioteca de fotos salva.

Jab
fonte

Respostas:

233

Considerando a verificação da exibição da retina, use o seguinte trecho de código:

#import <QuartzCore/QuartzCore.h> 

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

[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *imageData = UIImagePNGRepresentation(image);
if (imageData) {
    [imageData writeToFile:@"screenshot.png" atomically:YES];
} else {
    NSLog(@"error while taking screenshot");
}
DenNukem
fonte
13
Observe que isso não captura certos tipos de conteúdo da tela, como as camadas do OpenGL ES. UIGetScreenImage (), que Hua-Ying aponta para, faz.
Brad Larson
12
Queridas pessoas, não se esqueça de fazer #import <QuartzCore / QuartzCore.h> #
Enrico Susatyo
isso fará uma captura de tela se você estiver vendo uma foto que seu aplicativo acabou de tirar?
John Riselvato
5
Onde esta imagem é armazenada?
Burhanuddin Sunelwala
3
@ wladimir-palant: E quanto ao teclado, porque quando eu uso a captura de tela do cenário onde o usuário digita, o teclado não captura, como posso adicionar o teclado na captura de tela?
G212gs
46

O método abaixo também funciona para objetos OPENGL

//iOS7 or above
- (UIImage *) screenshot {

    CGSize size = CGSizeMake(your_width, your_height);

    UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);

    CGRect rec = CGRectMake(0, 0, your_width, your_height);
    [_viewController.view drawViewHierarchyInRect:rec afterScreenUpdates:YES];

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
Mann
fonte
5
UIGetScreenImageé uma API privada
jonahb
Muito obrigado por isso! Você definitivamente recebe meu voto. Depois de tentar várias outras maneiras, parece que drawViewHierarchy é a única maneira de capturar uma tela no robovm com uma interface de usuário javafx.
precisa saber é o seguinte
Isto é o que está funcionando sempre !!!! Eu tenho muitas transformações 3D na tela e outras soluções mexeram na minha captura de tela. Isso funciona como deveria. Muito obrigado!
AndrewK
1
Se o controlador de vista é em um comando de navegação a barra de navegação não é capturada
Radu Simionescu
Sim, drawViewHierarchyInRect é mais rápido que renderInContext :-)
alones
20
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, self.view.opaque, 0.0);
[self.myView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

NSData *imageData = UIImageJPEGRepresentation(image, 1.0 ); //you can use PNG too
[imageData writeToFile:@"image1.jpeg" atomically:YES];
IOS Rocks
fonte
1
+1 Para a observação sobre o uso de PNG. É sem perdas e será menor que jpeg com qualidade = 1.0.
Rob
1
não está usando o NavigationBar no iOS 7, mas deixando espaço em branco.
Ali Sufyan
14
- (UIImage*) getGLScreenshot {
    NSInteger myDataLength = 320 * 480 * 4;

    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

    // gl renders "upside down" so swap top to bottom into new array.
    // there's gotta be a better way, but this works.
    GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
    for(int y = 0; y <480; y++)
    {
        for(int x = 0; x <320 * 4; x++)
        {
            buffer2[(479 - y) * 320 * 4 + x] = buffer[y * 4 * 320 + x];
        }
    }

    // make data provider with data.
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);

    // prep the ingredients
    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4 * 320;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

    // make the cgimage
    CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);

    // then make the uiimage from that
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];
    return myImage;
}

- (void)saveGLScreenshotToPhotosAlbum {
    UIImageWriteToSavedPhotosAlbum([self getGLScreenshot], nil, nil, nil);  
}

Fonte .

Aistina
fonte
1
Por que myDataLength é multiplicado por 4 ontop de 320 * 480?
Jab
2
Isso também parece realmente muito estúpido, pois eles não nos fornecem uma maneira mais fácil de fazer isso.
Jab
320 * 480 é o número de pixels. Vezes quatro, para que você tenha RGBA para cada pixel.
Aistina
lembre-se de multiplicar pelo fator de escala ao executar em um iPhone 4 :-)
MrDatabase
1
@Aistina, use UIImage *myImage = [[UIImage alloc] initWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationDownMirrored];e você não precisa mais de 'buffer2'. BTW, parece-me que você se esqueceu de gerenciamento de memória, não livre () ou release () chamadas ...
Oleg Trakhman
10

EM SWIFT

func captureScreen() -> UIImage
{

    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0);

    self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)

    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return image
}
Vicky
fonte
9

Veja este post, parece que você pode usar UIGetScreenImage () por enquanto.

Hua-Ying
fonte
9
Isso não é permitido novamente no iOS 4.
sudo rm -rf
9

A partir do iOS10, isso fica um pouco mais simples. O UIKit vem com o UIGraphicsImageRenderque permite

... realize tarefas de desenho, sem ter que lidar com configurações como profundidade de cor e escala de imagem ou gerenciar contextos de gráficos principais

Apple Docs - UIGraphicsImageRenderer

Agora você pode fazer algo assim:

let renderer = UIGraphicsImageRenderer(size: someView.bounds.size)

let image = renderer.image(actions: { context in
    someView.drawHierarchy(in: someView.bounds, afterScreenUpdates: true)
})

Muitas das respostas aqui funcionaram para mim na maioria dos casos. Mas, ao tentar tirar um instantâneo de um ARSCNView, só consegui fazê-lo usando o método descrito acima. Embora possa valer a pena notar que, no momento, o ARKit ainda está na versão beta e o Xcode está na versão 4

Tulio Troncoso
fonte
7

Isso salvará uma captura de tela e também a devolverá.

-(UIImage *)capture{
    UIGraphicsBeginImageContext(self.view.bounds.size);
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *imageView = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIImageWriteToSavedPhotosAlbum(imageView, nil, nil, nil); //if you need to save
    return imageView;
}
A. Adam
fonte
5

Acho que o seguinte snippet ajudaria se você quiser fazer uma tela cheia ( exceto a barra de status ), basta substituir AppDelegate pelo nome do delegado do aplicativo, se necessário.

- (UIImage *)captureFullScreen {

    AppDelegate *_appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        // for retina-display
        UIGraphicsBeginImageContextWithOptions(_appDelegate.window.bounds.size, NO, [UIScreen mainScreen].scale);
        [_appDelegate.window drawViewHierarchyInRect:_appDelegate.window.bounds afterScreenUpdates:NO];
    } else {
        // non-retina-display
        UIGraphicsBeginImageContext(_bodyView.bounds.size);
        [_appDelegate.window drawViewHierarchyInRect:_appDelegate.window.bounds afterScreenUpdates:NO];
    }

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
tounaobun
fonte
Finalmente, um que adiciona a barra de navegação! Votaria duas vezes se eu pudesse! Cheers mate
marcelosarquis
4

Não consegui encontrar uma resposta com a implementação do Swift 3. Então aqui vai.

static func screenshotOf(window: UIWindow) -> UIImage? {

    UIGraphicsBeginImageContextWithOptions(window.bounds.size, true, UIScreen.main.scale)

    guard let currentContext = UIGraphicsGetCurrentContext() else {
        return nil
    }

    window.layer.render(in: currentContext)
    guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
        UIGraphicsEndImageContext()
        return nil
    }

    UIGraphicsEndImageContext()
    return image
}
jarora
fonte
3

Para iOS 7.0 ou superior ..

Se você quiser capturar imagens da tela, digamos (myView), faça isso com uma única linha:

[myView snapshotViewAfterScreenUpdates:NO];
sandeep gupta
fonte
3

Isso funcionará com o Swift 4.2 , a captura de tela será salva na biblioteca, mas não se esqueça de editar o info.plist @ NSPhotoLibraryAddUsageDescription:

  @IBAction func takeScreenshot(_ sender: UIButton) {

    //Start full Screenshot
    print("full Screenshot")
    UIGraphicsBeginImageContext(card.frame.size)
    view.layer.render(in: UIGraphicsGetCurrentContext()!)
    var sourceImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    UIImageWriteToSavedPhotosAlbum(sourceImage!, nil, nil, nil)

    //Start partial Screenshot
    print("partial Screenshot")
    UIGraphicsBeginImageContext(card.frame.size)
    sourceImage?.draw(at: CGPoint(x:-25,y:-100)) //the screenshot starts at -25, -100
    var croppedImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    UIImageWriteToSavedPhotosAlbum(croppedImage!, nil, nil, nil)

}
Just Lenz
fonte
2

Obter captura de tela da exibição

-(UIImage *)getScreenshotImage {
    if ([[UIScreen mainScreen] scale] == 2.0) {
        UIGraphicsBeginImageContextWithOptions(self.view.frame.size, FALSE, 2.0);
    } else {
        UIGraphicsBeginImageContextWithOptions(self.view.frame.size, FALSE, 1.0);
    }

    [self.view.window.layer renderInContext:UIGraphicsGetCurrentContext()];

    UIImage * result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return result;
}

Salvar imagem nas fotos

UIImageWriteToSavedPhotosAlbum(YOUR_IMAGE, nil, nil, nil);

Como

UIImageWriteToSavedPhotosAlbum([self getScreenshotImage], nil, nil, nil);
Fernando Cervantes
fonte
2

Obter captura de tela da exibição:

- (UIImage *)takeSnapshotView {
    CGRect rect = [myView bounds];//Here you can change your view with myView
    UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [myView.layer renderInContext:context];
    UIImage *capturedScreen = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return capturedScreen;//capturedScreen is the image of your view
}

Espero que seja isso que você está procurando. Qualquer preocupação volte para mim. :)

Conheça Doshi
fonte
2

Estou respondendo a essa pergunta, pois ela é altamente visualizada, e há muitas respostas por aí, além de Swift e Obj-C.

Isenção de responsabilidade Este não é o meu código, nem minhas respostas, é apenas para ajudar as pessoas que chegam aqui a encontrar uma resposta rápida. Existem links para as respostas originais para dar crédito onde o crédito é devido !! Honre as respostas originais com +1 se você usar a resposta!


Usando QuartzCore

#import <QuartzCore/QuartzCore.h> 

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

[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *imageData = UIImagePNGRepresentation(image);
if (imageData) {
    [imageData writeToFile:@"screenshot.png" atomically:YES];
} else {
    NSLog(@"error while taking screenshot");
}

Na Swift

func captureScreen() -> UIImage
{

    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0);

    self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)

    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return image
}

Nota: Como a natureza da programação, as atualizações podem precisar ser feitas, por isso edite ou me avise! * Além disso, se eu não incluir uma resposta / método que valha a pena incluir, sinta-se à vontade para me informar também!

Jab
fonte
1
Esta resposta parece não adicionar nada. Você apenas copiou e colou duas das respostas já na página e postou o resultado como seu.
User2357112 suporta Monica
Minha intenção não era levar o crédito por suas respostas, era incluir o Swift e o Obj-C em uma resposta, para que as pessoas encontrassem mais rapidamente. Também me referi às respostas correspondentes. se isso não for feito, excluirei minha resposta e voltarei a apenas uma delas.
Jab
Estou usando a solução ObjC. Para mim, eu tive que adicionar ".view" depois de "self", mas o resultado é 100%. Este é GOLD
samouray 24/09/19
Também funcionou no Capturing Presentation Stream. Obrigado
YSR fan
1

Outra opção é usar a ferramenta Automação em instrumentos. Você escreve um script para colocar a tela no estado que deseja e depois tira as fotos. Aqui está o script que usei para um dos meus aplicativos. Obviamente, os detalhes do script serão diferentes para o seu aplicativo.

var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();
var picker = window.pickers()[0];
var wheel = picker.wheels()[2];
var buttons = window.buttons();
var button1 = buttons.firstWithPredicate("name == 'dateButton1'");
var button2 = buttons.firstWithPredicate("name == 'dateButton2'");

function setYear(picker, year) {
    var yearName = year.toString();
    var yearWheel = picker.wheels()[2];
    yearWheel.selectValue(yearName);
}

function setMonth(picker, monthName) {
    var wheel = picker.wheels()[0];
    wheel.selectValue(monthName);
}

function setDay(picker, day) {
    var wheel = picker.wheels()[1];
    var name = day.toString();
    wheel.selectValue(name);
}

target.delay(1);
setYear(picker, 2015);
setMonth(picker, "July");
setDay(picker, 4);
button1.tap();
setYear(picker, 2015);
setMonth(picker, "December");
setDay(picker, 25);

target.captureScreenWithName("daysShot1");

var nButtons = buttons.length;
UIALogger.logMessage(nButtons + " buttons");
for (var i=0; i<nButtons; i++) {
    UIALogger.logMessage("button " + buttons[i].name());
}

var tabBar = window.tabBars()[0];
var barButtons = tabBar.buttons();

var nBarButtons = barButtons.length;
UIALogger.logMessage(nBarButtons + " buttons on tab bar");

for (var i=0; i<nBarButtons; i++) {
    UIALogger.logMessage("button " + barButtons[i].name());
}

var weeksButton = barButtons[1];
var monthsButton = barButtons[2];
var yearsButton = barButtons[3];

target.delay(2);
weeksButton.tap();
target.captureScreenWithName("daysShot2");
target.delay(2);
monthsButton.tap();
target.captureScreenWithName("daysShot3");
target.delay(2);
yearsButton.tap();
target.delay(2);
button2.tap();
target.delay(2);
setYear(picker, 2018);
target.delay(2);
target.captureScreenWithName("daysShot4");
William Jockusch
fonte
1

Duas opções disponíveis no site abaixo:

OPÇÃO 1: usando o UIWindow (testado e funcionando perfeitamente)

// create graphics context with screen size
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContext(screenRect.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] set];
CGContextFillRect(ctx, screenRect);

// grab reference to our window
UIWindow *window = [UIApplication sharedApplication].keyWindow;

// transfer content into our context
[window.layer renderInContext:ctx];
UIImage *screengrab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

OPÇÃO 2: usando UIView

// grab reference to the view you'd like to capture
UIView *wholeScreen = self.splitViewController.view;

// define the size and grab a UIImage from it
UIGraphicsBeginImageContextWithOptions(wholeScreen.bounds.size, wholeScreen.opaque, 0.0);
[wholeScreen.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screengrab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Para tela retina (como resposta DenNukem)

// grab reference to our window
    UIWindow *window = [UIApplication sharedApplication].keyWindow;

    // create graphics context with screen size
    CGRect screenRect = [[UIScreen mainScreen] bounds];
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(screenRect.size, NO, [UIScreen mainScreen].scale);
    } else {
        UIGraphicsBeginImageContext(screenRect.size);
        [window.layer renderInContext:UIGraphicsGetCurrentContext()];
    }

para obter mais detalhes: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatically/

Nhat Dinh
fonte
1

Apenas uma pequena contribuição, eu fiz isso com um botão, mas pressionar também significa que o botão é capturado pressionado. Então, primeiro eu não destaque.

- (IBAction)screenShot:(id)sender {
    // Unpress screen shot button
    screenShotButton.highlighted = NO;

    // create graphics context with screen size
    CGRect screenRect = [[UIScreen mainScreen] bounds];

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

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [[UIColor blackColor] set];
    CGContextFillRect(ctx, screenRect);

    // grab reference to our window
    UIWindow *window = [UIApplication sharedApplication].keyWindow;

    // transfer content into our context
    [window.layer renderInContext:ctx];
    UIImage *screengrab = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // save screengrab to Camera Roll
    UIImageWriteToSavedPhotosAlbum(screengrab, nil, nil, nil);
}

Eu obtive o corpo principal do código em: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatically/ onde usei a opção 1, a opção 2 parecia não funcionar para mim. Foram adicionados os ajustes para os tamanhos de tela da Rentina a partir deste segmento e o destaque do screenShotButton. A visualização em que estou usando é uma tela de botões e etiquetas do StoryBoard e com vários UIView adicionados posteriormente através do programa.

Symanski
fonte
Obrigado the_UB pela edição. Eu percebi que havia deixado o código comentado, mas depois pensei "é assim que o código colado é genuíno". Código de trabalho ativo que agora está na App Store.
Symanski
1

No Swift, você pode usar o seguinte código.

if UIScreen.mainScreen().respondsToSelector(Selector("scale")) {
    UIGraphicsBeginImageContextWithOptions(self.window!.bounds.size, false, UIScreen.mainScreen().scale)
}
else{
    UIGraphicsBeginImageContext(self.window!.bounds.size)
}
self.window?.layer.renderInContext(UIGraphicsGetCurrentContext())
var image : UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
Mohammad Sadiq Shaikh
fonte
0

Swift 4:

func makeImage(withView view: UIView) -> UIImage? {

    let rect = view.bounds

    UIGraphicsBeginImageContextWithOptions(rect.size, true, 0)

    guard let context = UIGraphicsGetCurrentContext() else {
      assertionFailure()
      return nil
    }

    view.layer.render(in: context)

    guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
      assertionFailure()
      return nil
    }

    UIGraphicsEndImageContext()

    return image
}
Chris Livdahl
fonte