Cómo tomar una captura de pantalla mediante programación en iOS

157

Quiero una captura de pantalla de la imagen en la pantalla guardada en la biblioteca de fotos guardada.

Pinchazo
fuente

Respuestas:

233

Teniendo en cuenta la comprobación de la pantalla retina, use el siguiente fragmento 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
fuente
13
Tenga en cuenta que esto no capturará ciertos tipos de contenido de pantalla, como las capas de OpenGL ES. UIGetScreenImage (), que señala Hua-Ying, lo hace.
Brad Larson
12
Querida gente, no olviden hacer #import <QuartzCore / QuartzCore.h>
Enrico Susatyo
¿tomará esto una captura de pantalla si está viendo una foto que acaba de tomar su aplicación?
John Riselvato
55
¿Dónde se almacena esta imagen?
Burhanuddin Sunelwala
3
@ wladimir-palant: ¿Qué pasa con el teclado, porque cuando uso para tomar una captura de pantalla de la pantalla donde el usuario escribe, luego el teclado no captura, cómo puedo agregar el teclado en la captura de pantalla?
g212gs
46

El siguiente método también 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
fuente
55
UIGetScreenImagees una API privada
jonahb
¡Muchas gracias por esto! Definitivamente tienes mi voto. Después de intentar varias otras formas, parece que drawViewHierarchy es la única forma en que se puede tomar una captura de pantalla en robovm con una interfaz de usuario javafx.
Christo Smal
¡Esto es lo que funciona cada vez! Tengo muchas transformaciones 3D en el scrreen y otras soluciones se metieron en mi captura de pantalla. Esto funciona como debería. ¡Muchas gracias!
AndrewK
1
si el controlador de vista está en un controlador de navegación, la barra de navegación no se captura
Radu Simionescu
Sí, drawViewHierarchyInRect es más rápido que renderInContext :-)
solo
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
fuente
1
+1 Para la nota sobre el uso de PNG. No tiene pérdidas y será más pequeño que JPEG con calidad = 1.0.
Rob
1
no está tomando NavigationBar en iOS 7, sino que deja espacio en blanco.
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);  
}

Fuente .

Aistina
fuente
1
¿Por qué myDataLength se multiplica por 4 sobre 320 * 480?
Jab
2
Esto también parece muy estúpido porque no nos proporcionan una forma más fácil de hacerlo.
Jab
320 * 480 es el número de píxeles. Veces por cuatro para que tengas RGBA por cada píxel.
Aistina
recuerde múltiple por el factor de escala cuando se ejecuta en un iPhone 4 :-)
MrDatabase
1
@Aistina, usa UIImage *myImage = [[UIImage alloc] initWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationDownMirrored];y ya no necesitas 'buffer2'. Por cierto, me parece que te olvidaste de la administración de memoria, no hay llamadas gratis () o release () ...
Oleg Trakhman
10

EN 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
fuente
9

Mira esta publicación, parece que puedes usar UIGetScreenImage () por ahora.

Hua-Ying
fuente
9
Esto no se permite nuevamente en iOS 4.
sudo rm -rf
9

A partir de iOS10, esto se vuelve un poco más simple. UIKit viene con UIGraphicsImageRendereso te permite

... realizar tareas de dibujo, sin tener que manejar la configuración, como la profundidad de color y la escala de la imagen, o administrar contextos de Core Graphics

Documentos de Apple - UIGraphicsImageRenderer

Entonces ahora puedes hacer algo como esto:

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

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

Muchas de las respuestas aquí me funcionaron en la mayoría de los casos. Pero cuando intenté tomar una instantánea de un ARSCNView, solo pude hacerlo usando el método descrito anteriormente. Aunque vale la pena señalar que en este momento, ARKit todavía está en beta y Xcode está en beta 4

Tulio Troncoso
fuente
7

Esto guardará una captura de pantalla y también devolverá la captura de pantalla.

-(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
fuente
5

Creo que el siguiente fragmento ayudaría si desea tomar una pantalla completa ( excepto la barra de estado ), simplemente reemplace AppDelegate con el nombre de delegado de su aplicación si es necesario.

- (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
fuente
¡Finalmente uno que agrega la barra de navegación! ¡Votaría dos veces si pudiera! Compañero de las aclamaciones
marcelosarquis
4

No pude encontrar una respuesta con la implementación de Swift 3. Así que aquí va.

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
fuente
3

Para iOS 7.0 o superior ...

Si desea tomar capturas de pantalla de view say (myView), puede hacerlo con una sola línea:

[myView snapshotViewAfterScreenUpdates:NO];
sandeep gupta
fuente
3

Esto funcionará con Swift 4.2 , la captura de pantalla se guardará en la biblioteca, pero no olvide editar 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)

}
Solo Lenz
fuente
2

Obtener captura de pantalla de la vista

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

Guardar imagen en fotos

UIImageWriteToSavedPhotosAlbum(YOUR_IMAGE, nil, nil, nil);

Cómo

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

Obtener captura de pantalla de la vista:

- (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, esto es lo que estás buscando. Cualquier inquietud vuelve a mí. :)

Conoce a Doshi
fuente
2

Estoy respondiendo esta pregunta, ya que es muy vista, y hay muchas respuestas, además de Swift y Obj-C.

Descargo de responsabilidad Este no es mi código, ni mis respuestas, esto es solo para ayudar a las personas que llegan aquí a encontrar una respuesta rápida. ¡Hay enlaces a las respuestas originales para dar crédito donde se debe crédito! ¡Honra las respuestas originales con un +1 si usas su respuesta!


Utilizando 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");
}

En veloz

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 la naturaleza de la programación, es posible que sea necesario realizar actualizaciones , así que edítelo o avíseme. * Además, si no incluí una respuesta / método que valga la pena, ¡no dudes en hacérmelo saber también!

Pinchazo
fuente
1
Esta respuesta no parece agregar nada. Acabas de copiar y pegar dos de las respuestas que ya están en la página y publicaste el resultado como tuyo.
user2357112 es compatible con Monica el
Mi intención no era tomar el crédito por sus respuestas, era incluir Swift y Obj-C en una sola respuesta para que la gente pudiera encontrarla más rápido. También me referí a las respuestas correspondientes. Si esto no se hace, eliminaré mi respuesta y volveré a una de ellas.
Jab
Estoy usando la solución ObjC. Para mí, tuve que agregar ".view" después de "self", pero el resultado es 100%. Esto es ORO
samouray
Funcionó para Capturar Presentation Stream también. Gracias
fan de YSR
1

Otra opción es usar la herramienta de automatización en instrumentos. Escribes un guión para colocar la pantalla en lo que quieras, luego tomas las fotos. Aquí está el script que usé para una de mis aplicaciones. Obviamente, los detalles del script serán diferentes para su aplicación.

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
fuente
1

Dos opciones disponibles en el siguiente sitio:

OPCIÓN 1: usar UIWindow (probado y funciona perfectamente)

// 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();

OPCIÓN 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 la pantalla de retina (como responde 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 más detalles: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatic/

Nhat Dinh
fuente
1

Solo una pequeña contribución, lo he hecho con un botón, pero al presionar también se captura el botón presionado. Así que primero destaco.

- (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);
}

Obtuve el cuerpo principal del código de: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatic/ donde utilicé la opción 1, la opción 2 no parecía funcionar para mi. Se agregaron los ajustes para los tamaños de pantalla de Rentina de este hilo y la falta de resaltado de screenShotButton. La vista en la que lo estoy usando es una pantalla de botones y etiquetas de StoryBoarded y con varias UIView agregadas más tarde a través del programa.

Symanski
fuente
Gracias the_UB por la edición. Me di cuenta de que había dejado el código comentado, pero luego pensé "así de genuino es el código pegado". Código de trabajo en vivo que ahora está en la App Store.
Symanski,
1

En Swift puedes usar el siguiente 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
fuente
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
fuente