So erstellen Sie programmgesteuert einen Screenshot unter iOS


157

Ich möchte einen Screenshot des Bildes auf dem Bildschirm in der gespeicherten Fotobibliothek speichern.

Antworten:


233

Verwenden Sie für eine Überprüfung der Netzhautanzeige den folgenden Codeausschnitt:

#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
Beachten Sie, dass dadurch bestimmte Arten von Bildschirminhalten wie OpenGL ES-Ebenen nicht erfasst werden. UIGetScreenImage (), auf das Hua-Ying zeigt, tut dies.
Brad Larson

12
Liebe Leute, vergessen Sie nicht, #import <QuartzCore / QuartzCore.h> zu tun
Enrico Susatyo

Wird ein Screenshot erstellt, wenn Sie ein Bild anzeigen, das Ihre App gerade aufgenommen hat?
John Riselvato

5
Wo ist dieses Bild gespeichert?
Burhanuddin Sunelwala

3
@ wladimir-palant: Was ist mit Tastatur, denn wenn ich einen Screenshot von sceen mache, in dem der Benutzertyp, dann Tastatur nicht erfasst, wie kann ich Tastatur in Bildschirmaufnahme hinzufügen ..?
g212gs

46

Die folgende Methode funktioniert auch für OPENGL-Objekte

//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
UIGetScreenImageist eine private API
Jonahb

Vielen Dank dafür! Du bekommst definitiv meine Stimme. Nach mehreren anderen Versuchen scheint drawViewHierarchy der einzige Weg zu sein, wie ein Screenshot in robovm mit einer javafx-Benutzeroberfläche erstellt werden kann.
Christo Smal

Das ist es, was jedes Mal funktioniert !!!! Ich habe viele 3D-Transformationen auf dem Bildschirm und andere Lösungen haben meinen Screenshot durcheinander gebracht. Das funktioniert wie es sollte. Vielen Dank!
AndrewK

1
Befindet sich der View Controller in einem Navigationscontroller, wird die Navigationsleiste nicht erfasst
Radu Simionescu

Ja, drawViewHierarchyInRect ist schneller als renderInContext :-)
allein

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 Für den Hinweis zur Verwendung von PNG. Es ist verlustfrei und kleiner als JPEG mit einer Qualität von 1,0.
Rob

1
In iOS 7 wird keine Navigationsleiste verwendet, sondern es wird ein Leerzeichen gelassen.
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);  
}

Quelle .


1
Warum wird myDataLength mit 4 auf 320 * 480 multipliziert?
Jab

2
Das scheint auch wirklich sehr dumm zu sein, dass sie uns keinen einfacheren Weg bieten, dies zu tun.
Jab

320 * 480 ist die Anzahl der Pixel. Mal vier, also hast du RGBA für jedes Pixel.
Aistina


1
@Aistina, benutze UIImage *myImage = [[UIImage alloc] initWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationDownMirrored];und du brauchst 'buffer2' nicht mehr. Übrigens scheint es mir, als hätten Sie die Speicherverwaltung vergessen, keine kostenlosen () oder release () -Aufrufe ...
Oleg Trakhman

10

IN SCHNELL

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

Ab iOS10 wird dies etwas einfacher. UIKit wird mitgeliefert UIGraphicsImageRender, damit Sie es können

... Zeichnungsaufgaben ausführen, ohne Konfigurationen wie Farbtiefe und Bildskalierung vornehmen oder Core Graphics-Kontexte verwalten zu müssen

Apple Docs - UIGraphicsImageRenderer

Sie können jetzt so etwas tun:

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

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

Viele der Antworten hier haben in den meisten Fällen für mich funktioniert. Aber als ARSCNViewich versuchte, einen Schnappschuss von einem zu machen , konnte ich das nur mit der oben beschriebenen Methode machen. Obwohl es erwähnenswert sein könnte, dass sich ARKit derzeit noch in der Beta befindet und Xcode sich in der Beta 4 befindet


7

Dadurch wird ein Screenshot gespeichert und der Screenshot wird ebenfalls zurückgegeben.

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

Ich denke, das folgende Snippet würde helfen, wenn Sie einen Vollbildmodus (mit Ausnahme der Statusleiste ) erstellen möchten. Ersetzen Sie AppDelegate bei Bedarf durch den Namen Ihres App-Delegaten.

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

Endlich eine, die die Navigationsleiste hinzufügt! Würde zweimal abstimmen, wenn ich könnte! Prost Kumpel
marcelosarquis

4

Ich konnte mit der Implementierung von Swift 3 keine Antwort finden. Also los geht's.

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

Für iOS 7.0 oder höher ..

Wenn Sie beispielsweise Screenshots der Ansicht (myView) machen möchten, können Sie dies mit einer einzelnen Zeile tun:

[myView snapshotViewAfterScreenUpdates:NO];

3

Dies funktioniert mit Swift 4.2 . Der Screenshot wird in der Bibliothek gespeichert. Vergessen Sie jedoch nicht, Folgendes zu bearbeiten 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

Screenshot aus der Ansicht abrufen

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

Bild auf Fotos speichern

UIImageWriteToSavedPhotosAlbum(YOUR_IMAGE, nil, nil, nil);

Wie man

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

2

Screenshot aus der Ansicht abrufen:

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

Hoffe, das ist was du suchst. Alle Bedenken melden sich bei mir. :) :)


2

Ich beantworte diese Frage, da sie sehr geschätzt wird und es viele Antworten gibt, außerdem gibt es Swift und Obj-C.

Haftungsausschluss Dies ist weder mein Code noch meine Antwort. Dies soll nur Menschen helfen, die hier landen, eine schnelle Antwort zu finden. Es gibt Links zu den ursprünglichen Antworten, um Kredit zu geben, wo Kredit fällig ist !! Bitte würdigen Sie die ursprünglichen Antworten mit +1, wenn Sie ihre Antwort verwenden!


Verwenden von 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
}

Hinweis: Aufgrund der Art der Programmierung müssen möglicherweise Aktualisierungen vorgenommen werden. Bearbeiten Sie diese oder lassen Sie es mich wissen! * Auch wenn ich keine Antwort / Methode angegeben habe, die es wert ist, aufgenommen zu werden, können Sie mich auch darüber informieren!


1
Diese Antwort scheint nichts hinzuzufügen. Sie haben gerade zwei der bereits auf der Seite vorhandenen Antworten kopiert und das Ergebnis als Ihr eigenes veröffentlicht.
user2357112 unterstützt Monica

Meine Absicht war es, ihre Antworten nicht zu würdigen, sondern sowohl Swift als auch Obj-C in eine Antwort aufzunehmen, damit die Leute sie schneller finden konnten. Ich habe auch auf die entsprechenden Antworten verwiesen. Sollte dies nicht geschehen, werde ich meine Antwort löschen und auf nur eine davon zurückgreifen.
Jab

Ich verwende die ObjC-Lösung. Für mich musste ich ".view" nach "self" hinzufügen, aber das Ergebnis ist 100%. Dies ist
Gold

Es funktionierte auch für die Erfassung von Presentation Stream. Danke
YSR-Fan

1

Eine weitere Option ist die Verwendung des Automatisierungstools für Instrumente. Sie schreiben ein Skript, um den Bildschirm in den gewünschten Zustand zu versetzen, und machen dann die Aufnahmen. Hier ist das Skript, das ich für eine meiner Apps verwendet habe. Offensichtlich unterscheiden sich die Details des Skripts für Ihre 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

Zwei Optionen sind unten aufgeführt:

OPTION 1: Verwenden von UIWindow (versucht und funktioniert perfekt)

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

Für Retina-Bildschirm (als DenNukem-Antwort)

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

Für weitere Informationen: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatic/


1

Nur ein kleiner Beitrag, ich habe dies mit einem Knopf gemacht, aber das Drücken bedeutet auch, dass der Knopf gedrückt wird. Also mache ich zuerst ein Highlight.

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

Ich habe den Hauptteil des Codes von folgender Adresse erhalten : http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatisch/ wo ich Option 1 verwendet habe, Option 2 schien nicht zu funktionieren für mich. Die Anpassungen für Rentina-Bildschirmgrößen aus diesem Thread und die Hervorhebung des screenShotButton wurden hinzugefügt. Die Ansicht, in der ich sie verwende, ist ein StoryBoarded-Bildschirm mit Schaltflächen und Beschriftungen, und mehrere UIView werden später über das Programm hinzugefügt.


Danke the_UB für die Bearbeitung. Ich hatte festgestellt, dass ich einen kommentierten Code hinterlassen hatte, dachte dann aber, "so echt ist der eingefügte Code". Live-Arbeitscode, der jetzt im App Store verfügbar ist.
Symanski

1

In Swift können Sie folgenden Code verwenden.

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
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.