Comment faire une capture d'écran par programmation sur iOS

157

Je veux une capture d'écran de l'image sur l'écran enregistrée dans la photothèque enregistrée.

Coup
la source

Réponses:

233

Si vous envisagez de vérifier l' affichage de la rétine, utilisez l'extrait de code suivant:

#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
la source
13
Notez que cela ne capturera pas certains types de contenu d'écran, comme les couches OpenGL ES. UIGetScreenImage (), vers lequel pointe Hua-Ying, fait.
Brad Larson
12
Chers gens, n'oubliez pas de faire #import <QuartzCore / QuartzCore.h>
Enrico Susatyo
cela prendra-t-il une capture d'écran si vous regardez une photo que votre application vient de prendre?
John Riselvato
5
Où cette image est-elle stockée?
Burhanuddin Sunelwala
3
@ wladimir-palant: Qu'en est-il du clavier, parce que lorsque j'utilise pour prendre une capture d'écran d'un écran où le type d'utilisateur, puis le clavier ne capture pas, comment puis-je ajouter un clavier dans la capture d'écran ..?
g212gs
46

La méthode ci-dessous fonctionne également pour les objets 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
la source
5
UIGetScreenImageest une API privée
jonahb
Merci beaucoup pour cela! Vous avez certainement mon vote. Après avoir essayé plusieurs autres façons, il semble que drawViewHierarchy soit le seul moyen de prendre une capture d'écran dans robovm avec une interface utilisateur javafx.
Christo Smal
C'est ce qui fonctionne à chaque fois !!!! J'ai beaucoup de transformations 3D sur l'écran et d'autres solutions ont gâché ma capture d'écran. Cela fonctionne comme il se doit. Merci beaucoup!
AndrewK
1
si le contrôleur de vue est dans un contrôleur de navigation, la barre de navigation n'est pas capturée
Radu Simionescu
Oui, drawViewHierarchyInRect est plus rapide que renderInContext :-)
seul
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
la source
1
+1 Pour la note sur l'utilisation de PNG. Il est sans perte et sera plus petit que jpeg avec une qualité = 1.0.
Rob
1
il ne prend pas NavigationBar dans iOS 7 au lieu de laisser un espace vide.
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);  
}

Source .

Aistina
la source
1
Pourquoi myDataLength est-il multiplié par 4 sur 320 * 480?
Jab
2
Cela semble aussi vraiment stupide qu'ils ne nous fournissent pas un moyen plus facile de le faire.
Jab
320 * 480 est le nombre de pixels. Quatre fois pour que vous ayez RGBA pour chaque pixel.
Aistina le
n'oubliez pas de multiplier par le facteur d'échelle lors de l'exécution sur un iPhone 4 :-)
MrDatabase
1
@Aistina, utilisez UIImage *myImage = [[UIImage alloc] initWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationDownMirrored];et vous n'avez plus besoin de 'buffer2'. BTW, il me semble que vous avez oublié la gestion de la mémoire, pas d'appels gratuits () ou release () ...
Oleg Trakhman
10

DANS 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
la source
9

Voir cet article, il semble que vous pouvez utiliser UIGetScreenImage () pour le moment.

Hua-Ying
la source
9
Cela n'est plus autorisé dans iOS 4.
sudo rm -rf
9

À partir d'iOS10, cela devient un peu plus simple. UIKit est livré avec UIGraphicsImageRenderqui vous permet de

... accomplir des tâches de dessin, sans avoir à gérer la configuration telle que la profondeur de couleur et l'échelle d'image, ou gérer les contextes Core Graphics

Documents Apple - UIGraphicsImageRenderer

Vous pouvez donc maintenant faire quelque chose comme ceci:

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

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

Beaucoup de réponses ici ont fonctionné pour moi dans la plupart des cas. Mais en essayant de prendre un instantané d'un ARSCNView, je n'ai pu le faire qu'en utilisant la méthode décrite ci-dessus. Bien qu'il puisse être intéressant de noter qu'à l'heure actuelle, ARKit est toujours en version bêta et Xcode est en version bêta 4

Tulio Troncoso
la source
7

Cela enregistrera une capture d'écran et retournera également la capture d'écran.

-(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
la source
5

Je pense que l'extrait de code suivant vous aiderait si vous souhaitez prendre un plein écran (à l' exception de la barre d'état ), remplacez simplement AppDelegate par le nom de votre délégué d'application si nécessaire.

- (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
la source
Enfin, celui qui ajoute la barre de navigation! Je voterais deux fois si je pouvais! Cheers mate
marcelosarquis
4

Je n'ai pas trouvé de réponse avec la mise en œuvre de Swift 3. Alors voilà.

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
la source
3

Pour iOS 7.0 ou supérieur.

Si vous souhaitez prendre des captures d'écran de la vue, disons (myView), vous pouvez le faire avec une seule ligne:

[myView snapshotViewAfterScreenUpdates:NO];
sandeep gupta
la source
3

Cela fonctionnera avec swift 4.2 , la capture d'écran sera enregistrée dans la bibliothèque, mais n'oubliez pas de modifier le 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)

}
Juste Lenz
la source
2

Obtenir une capture d'écran à partir de la vue

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

Enregistrer l'image dans les photos

UIImageWriteToSavedPhotosAlbum(YOUR_IMAGE, nil, nil, nil);

Comment

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

Obtenir une capture d'écran de la vue:

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

J'espère que c'est ce que vous recherchez. Toute inquiétude me revient. :)

Rencontrez Doshi
la source
2

Je réponds à cette question car c'est très apprécié, et il y a beaucoup de réponses là-bas, plus il y a Swift et Obj-C.

Avertissement Ce n'est pas mon code, ni mes réponses, c'est uniquement pour aider les gens qui débarquent ici à trouver une réponse rapide. Il y a des liens vers les réponses originales pour donner du crédit là où le crédit est dû !! Veuillez honorer les réponses originales avec un +1 si vous utilisez leur réponse!


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

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

Remarque: en raison de la nature de la programmation, des mises à jour peuvent être nécessaires, veuillez donc les modifier ou me le faire savoir! * Aussi si je n'ai pas inclus de réponse / méthode qui vaut la peine d'être incluse, n'hésitez pas à me le faire savoir également!

Coup
la source
1
Cette réponse ne semble rien ajouter. Vous venez de copier-coller deux des réponses déjà sur la page et de publier le résultat comme le vôtre.
user2357112 prend en charge Monica
Mon intention n'était pas de s'attribuer le mérite de leurs réponses, mais d'inclure à la fois Swift et Obj-C dans une seule réponse afin que les gens puissent les trouver plus rapidement. J'ai également évoqué les réponses correspondantes. si cela ne doit pas être fait, je supprimerai ma réponse et je reviendrai à une seule d'entre elles.
Jab
J'utilise la solution ObjC. Pour moi, j'ai dû ajouter ".view" après "self", mais le résultat est de 100%. C'est de l'
or
Cela a également fonctionné pour capturer le flux de présentation. Merci
Fan de YSR le
1

Une autre option consiste à utiliser l'outil d'automatisation sur les instruments. Vous écrivez un script pour mettre l'écran dans ce que vous voulez, puis prenez les photos. Voici le script que j'ai utilisé pour l'une de mes applications. Évidemment, les détails du script seront différents pour votre application.

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
la source
1

Deux options disponibles sur le site ci-dessous:

OPTION 1: utilisation d'UIWindow (essayé et fonctionne parfaitement)

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

OPTION 2: utiliser 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();

Pour l'écran de la rétine (comme réponse 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()];
    }

pour plus de détails: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatically/

Nhat Dinh
la source
1

Juste une petite contribution, je l'ai fait avec un bouton mais le fait d'appuyer signifie également que le bouton est capturé enfoncé. Alors d'abord, je désélectionne.

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

J'ai obtenu le corps principal du code sur: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatically/ où j'ai utilisé l'option 1, l'option 2 ne semblait pas fonctionner pour moi. Ajout des ajustements pour les tailles d'écran Rentina à partir de ce fil, et la désactivation du screenShotButton. La vue sur laquelle je l'utilise est un écran StoryBoarded de boutons et d'étiquettes et avec plusieurs UIView ajoutés ultérieurement via le programme.

Symanski
la source
Merci the_UB pour la modification. J'avais réalisé que j'avais laissé dans le code commenté, mais j'ai ensuite pensé "c'est à quel point le code collé est authentique". Code de travail en direct qui est maintenant dans l'App Store.
Symanski
1

Dans Swift, vous pouvez utiliser le code suivant.

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
la source
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
la source