Come fare uno screenshot a livello di codice su iOS


157

Voglio uno screenshot dell'immagine sullo schermo salvato nella libreria di foto salvata.

Risposte:


233

Considerando un controllo per la visualizzazione della retina, utilizzare il seguente frammento di codice:

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

13
Nota che questo non catturerà alcuni tipi di contenuti dello schermo, come i livelli OpenGL ES. UIGetScreenImage (), indicato da Hua-Ying.
Brad Larson

12
Cari persone, non dimenticate di fare #import <QuartzCore / QuartzCore.h>
Enrico Susatyo,

questo prenderà uno screenshot se stai visualizzando un'immagine appena scattata dalla tua app?
John Riselvato,

5
Dove è memorizzata questa immagine?
Burhanuddin Sunelwala,

3
@ wladimir-palant: Che dire della tastiera, perché quando uso una schermata di sceen in cui digitare l'utente, quindi la tastiera non viene catturata, come posso aggiungere la tastiera nella cattura dello schermo ..?
g212gs,

46

Il metodo seguente funziona anche con oggetti 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;
}

5
UIGetScreenImageè un'API privata
jonahb

Grazie mille per questo! Ottieni sicuramente il mio voto. Dopo aver tentato diversi altri modi, sembra che drawViewHierarchy sia l'unico modo per catturare una schermata in robovm con un'interfaccia utente javafx.
Christo Smal,

Questo è ciò che funziona ogni volta !!!! Ho molte trasformazioni 3d sullo schermo e altre soluzioni hanno fatto casino nel mio screenshot. Funziona come dovrebbe. Molte grazie!
AndrewK,

1
se il controller di visualizzazione si trova in un controller di navigazione, la barra di navigazione non viene acquisita
Radu Simionescu

Sì, drawViewHierarchyInRect è più veloce di 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];

1
+1 Per la nota sull'uso di PNG. È senza perdite e sarà più piccolo di jpeg con qualità = 1.0.
Rob

1
non sta prendendo NavigationBar in iOS 7 invece di lasciare uno spazio vuoto.
Ali Sufyan il

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 .


1
Perché myDataLength è moltiplicato per 4 in cima a 320 * 480?
Jab

2
Anche questo sembra davvero molto stupido che non ci forniscano un modo più semplice per farlo.
Jab

320 * 480 è il numero di pixel. Quattro volte, quindi hai RGBA per ogni pixel.
Aistina,

ricordarsi di moltiplicare per il fattore di scala quando si esegue su un iPhone 4 :-)
MrDatabase

1
@Aistina, usa UIImage *myImage = [[UIImage alloc] initWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationDownMirrored];e non hai più bisogno di 'buffer2'. A proposito, mi sembra di aver dimenticato la gestione della memoria, nessuna chiamata gratuita () o release () ...
Oleg Trakhman,

10

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


9

A partire da iOS10, questo diventa un po 'più semplice. UIKit viene fornito con UIGraphicsImageRenderciò che consente di

... eseguire attività di disegno, senza dover gestire configurazioni come la profondità del colore e la scala delle immagini, o gestire i contesti di grafica principale

Apple Docs - UIGraphicsImageRenderer

Quindi ora puoi fare qualcosa del genere:

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

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

Molte delle risposte qui hanno funzionato per me nella maggior parte dei casi. Ma quando ARSCNViewho provato a scattare un'istantanea di un , sono stato in grado di farlo solo usando il metodo sopra descritto. Anche se potrebbe valere la pena notare che in questo momento, ARKit è ancora in beta e Xcode è in beta 4


7

Ciò salverà uno screenshot e restituirà anche lo screenshot.

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

5

Penso che il seguente frammento sarebbe di aiuto se vuoi prendere uno schermo intero ( tranne la barra di stato ), se necessario sostituisci AppDelegate con il nome del delegato dell'app.

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

Finalmente uno che aggiunge la barra di navigazione! Vorrei votare due volte se potessi! Saluti compagno
marcelosarquis,

4

Non sono riuscito a trovare una risposta con l'implementazione di Swift 3. Quindi eccolo qui.

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
}

3

Per iOS 7.0 o versioni successive.

Se vuoi prendere screenshot di view dire (myView), puoi farlo con una sola riga:

[myView snapshotViewAfterScreenUpdates:NO];

3

Funzionerà con swift 4.2 , lo screenshot verrà salvato in libreria, ma non dimenticare di modificare 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)

}

2

Ottieni screenshot dalla 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;
}

Salva immagine in foto

UIImageWriteToSavedPhotosAlbum(YOUR_IMAGE, nil, nil, nil);

Come

UIImageWriteToSavedPhotosAlbum([self getScreenshotImage], nil, nil, nil);

2

Ottieni screenshot dalla 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
}

Spero, questo è quello che stai cercando. Qualsiasi preoccupazione mi risponda. :)


2

Sto rispondendo a questa domanda in quanto è molto apprezzato e ci sono molte risposte là fuori, oltre a Swift e Obj-C.

Disclaimer Questo non è il mio codice, né le mie risposte, questo è solo per aiutare le persone che arrivano qui a trovare una risposta rapida. Ci sono collegamenti alle risposte originali per dare credito dove il credito è dovuto !! Ti preghiamo di onorare le risposte originali con un +1 se usi la loro risposta!


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

In 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: come per la natura con la programmazione, potrebbe essere necessario eseguire gli aggiornamenti , per favore modifica o fammi sapere! * Anche se non riuscissi a includere una risposta / un metodo degno di nota, sentiti libero di farmelo sapere!


1
Questa risposta non sembra aggiungere nulla. Hai appena incollato due delle risposte già nella pagina e pubblicato il risultato come tuo.
user2357112 supporta Monica il

La mia intenzione non era quella di prendermi il merito delle loro risposte, era di includere sia rapido che Obj-C in una risposta, quindi era più veloce per le persone trovare. Ho fatto riferimento anche alle risposte corrispondenti. se ciò non dovesse essere fatto, eliminerò la mia risposta e tornerò a una sola di esse.
Jab il

Sto usando la soluzione ObjC. Per me, ho dovuto aggiungere ".view" dopo "self", ma il risultato è al 100%. Questo è GOLD
sabato

Ha funzionato anche per Capturing Presentation Stream. Grazie
Fan YSR il

1

Un'altra opzione è quella di utilizzare lo strumento di automazione sugli strumenti. Scrivi una sceneggiatura per mettere lo schermo in qualsiasi cosa tu voglia, quindi scatta le foto. Ecco lo script che ho usato per una delle mie app. Ovviamente, i dettagli dello script saranno diversi per la tua app.

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

1

Due opzioni disponibili sul seguente sito:

OPZIONE 1: utilizzo di UIWindow (provato e perfettamente funzionante)

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

OPZIONE 2: utilizzo di 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();

Per schermo retina (come risposta 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()];
    }

per maggiori dettagli: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatical/


1

Solo un piccolo contributo, l'ho fatto con un pulsante ma la pressione significa anche che il pulsante viene acquisito premuto. Quindi, per prima cosa, desidero evidenziare.

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

Ho ottenuto il corpo principale del codice da: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatical/ dove ho usato l'opzione 1, l'opzione 2 non sembra funzionare per me. Aggiunte le regolazioni per le dimensioni dello schermo di Rentina da questo thread e la non evidenziazione dello screenShotButton. La vista su cui lo sto usando è una schermata StoryBoarded di pulsanti ed etichette e con diverse UIView aggiunte in seguito tramite il programma.


Grazie the_UB per la modifica. Mi ero reso conto di aver lasciato nel codice commentato, ma poi ho pensato "è quanto sia genuino il codice incollato". Codice di lavoro attivo che si trova ora nell'App Store.
Symanski

1

In Swift è possibile utilizzare il seguente codice.

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)

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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.