Wie mache ich einen Vollbild-Screenshot in Swift?


73

Ich habe diesen Code gefunden :

func screenShotMethod() {
    //Create the UIImage
    UIGraphicsBeginImageContext(view.frame.size)
    view.layer.renderInContext(UIGraphicsGetCurrentContext())
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    //Save it to the camera roll
    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}

Was muss ich tun, um alle anderen Elemente wie die Navigationsleiste in die Screenshots aufzunehmen?


Wissen Sie, wie man in Ziel C einen Vollbild-Screenshot macht?
Mohit

Ja, aber ich kenne den relativen Code nicht.
Pietro La Spada

CALayer * layer = [[UIApplication sharedApplication] keyWindow] .layer; CGFloat scale = [UIScreen mainScreen] .scale; UIGraphicsBeginImageContextWithOptions (layer.frame.size, NO, scale); [Ebene renderInContext: UIGraphicsGetCurrentContext ()]; image = UIGraphicsGetImageFromCurrentImageContext (); Dies ist, um Vollbild-Screenshot in Ziel C zu machen, konvertieren Sie es als par swift
Mohit

Danke, aber es gibt keine Statusleiste.
Pietro La Spada

Antworten:


81

Lassen Sie mich erklären, was Ihr aktueller Code bewirkt und wie Sie ihn ändern können, um den gesamten Bildschirm zu erfassen, anstatt nur die Antwort zu veröffentlichen.

UIGraphicsBeginImageContext(view.frame.size)

Diese Codezeile erstellt einen neuen Bildkontext mit der gleichen Größe wie view. Die Hauptsache, die Sie hier mitnehmen sollten, ist, dass der neue Bildkontext dieselbe Größe hat wieview . Sofern Sie keine Version Ihrer Anwendung mit niedriger Auflösung (ohne Netzhaut) erfassen möchten, sollten Sie wahrscheinlich UIGraphicsBeginImageContextWithOptionsstattdessen verwenden. Dann können Sie übergeben 0.0, um den gleichen Skalierungsfaktor wie auf dem Hauptbildschirm des Geräts zu erhalten.

view.layer.renderInContext(UIGraphicsGetCurrentContext())

Diese Codezeile rendert die Ebene der Ansicht in den aktuellen Grafikkontext (den gerade erstellten Kontext). Die Hauptsache, die hier weggenommen werden muss, ist, dass nur view(und ihre Unteransichten) in den Bildkontext gezogen werden.

let image = UIGraphicsGetImageFromCurrentImageContext()

Diese Codezeile erstellt ein UIImage-Objekt aus dem, was in den Grafikkontext gezeichnet wurde.

UIGraphicsEndImageContext()

Diese Codezeile beendet den Bildkontext. Es wird bereinigt (Sie haben den Kontext erstellt und sollten ihn ebenfalls entfernen.


Das Ergebnis ist ein Bild mit der gleichen Größe wie viewmit viewund den darin gezeichneten Unteransichten.

Wenn Sie alles in das Bild zeichnen möchten, sollten Sie ein Bild erstellen, das der Größe des Bildschirms entspricht, und alles, was auf dem Bildschirm angezeigt wird, in das Bild zeichnen. In der Praxis sprechen Sie wahrscheinlich nur über alles im "Schlüsselfenster" Ihrer Anwendung. Da UIWindowes sich um eine Unterklasse von handelt UIView, kann sie auch in einen Bildkontext gezeichnet werden.


Wenn ich dies versuche, um den Bildschirm des Bildes zu erfassen, ist das Ergebnis ein Bild, das etwas pixelig ist. Ich kann nicht herausfinden warum. Hast du eine Idee, David?
Sam

1
Beachten Sie, dass ich keinen der OP-Codes geändert habe, sondern nur einen Schub in die richtige Richtung gegeben habe. Der pixelige Look, den Sie sehen, ergibt sich aus dem niedrigen Skalierungsfaktor des Bildkontexts. Wenn Sie die Dokumentation lesen, werden UIGraphicsBeginImageContextSie feststellen, dass sie UIGraphicsBeginImageContextWithOptionsmit einem Skalierungsfaktor von aufruft 1.0. Wenn Sie die Dokumentation lesen, erfahren Sie, dass Sie den Skalierungsfaktor so einstellen können 0.0, dass er mit Ihrem Hauptbildschirm übereinstimmt.
David Rönnqvist

Danke David. In der Tat hat es funktioniert, den Skalierungsfaktor auf 0 zu setzen.
Sam

Ich weiß, dass dies ein alter Thread ist, aber gibt es eine Möglichkeit, alles zu erfassen, was auf dem Bildschirm gerendert wird, einschließlich einer Warnung?
U84six

1
@AlexanderKhitev Ich denke nicht, dass es möglich ist, da die Tastatur nicht Teil Ihrer Anwendung ist. Es wäre ein Alptraum für die Privatsphäre, wenn Sie einen Screenshot des gesamten Bildschirms aufnehmen könnten (z. B. iPad mit zwei Apps im geteilten Bildschirm).
Ndreisg

62

Swift 4

    /// Takes the screenshot of the screen and returns the corresponding image
    ///
    /// - Parameter shouldSave: Boolean flag asking if the image needs to be saved to user's photo library. Default set to 'true'
    /// - Returns: (Optional)image captured as a screenshot
    open func takeScreenshot(_ shouldSave: Bool = true) -> UIImage? {
        var screenshotImage :UIImage?
        let layer = UIApplication.shared.keyWindow!.layer
        let scale = UIScreen.main.scale
        UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
        guard let context = UIGraphicsGetCurrentContext() else {return nil}
        layer.render(in:context)
        screenshotImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        if let image = screenshotImage, shouldSave {
            UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
        }
        return screenshotImage
    }

Aktualisiert für Swift 2

Der von Ihnen bereitgestellte Code funktioniert, aber Sie können das NavigationBarund das StatusBarin Ihrem Screenshot nicht erfassen . Wenn Sie einen Screenshot Ihres Geräts NavigationBarerstellen möchten, der Folgendes enthält, müssen Sie den folgenden Code verwenden:

func screenShotMethod() {
    let layer = UIApplication.sharedApplication().keyWindow!.layer
    let scale = UIScreen.mainScreen().scale
    UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);

    layer.renderInContext(UIGraphicsGetCurrentContext()!)
    let screenshot = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    UIImageWriteToSavedPhotosAlbum(screenshot, nil, nil, nil)
}

Mit diesem Code:

  • Wenn Sie die App zum ersten Mal starten und diese Methode aufrufen, werden Sie vom iOS-Gerät um die Erlaubnis gebeten, Ihr Bild in der Kamerarolle zu speichern.
  • Das Ergebnis dieses Codes ist ein JPG-Bild.
  • Das StatusBarwird im endgültigen Bild nicht angezeigt.

3
Tolle Methode! Dank der Skalierung wird das Bild in voller Netzhautauflösung erfasst. Vielen Dank für diesen Code! Yan-
Fox5150

1
Leider enthält dies KEINE CATransform3D, die Sie auf Ebenen im gesamten Bild angewendet haben.
Fattie

1
Unter Verwendung von Swift 3.0 unter iOS 10.x müssen Sie den Schlüssel <key> NSPhotoLibraryUsageDescription </ key> <string> Zu Ihrer Fotobibliothek </ string> zu Ihrer Info.plist hinzufügen
user3069232

1
Wie kann ich auch die Statusleiste in den Screenshot aufnehmen?
bLacK hoLE

1
Nicht wirklich korrekt, da Sie nil nicht zurückgeben können, wenn der Typ optional ist. Ihre Zeile "guard let context = UIGraphicsGetCurrentContext () else {return nil}" ist falsch
George Asda

35

Einzelheiten

  • Xcode 9.3, Swift 4.1
  • Xcode 10.2 (10E125) und 11.0 (11A420a), Swift 5

Getestet unter iOS: 9, 10, 11, 12, 13

Lösung

import UIKit

extension UIApplication {

    func getKeyWindow() -> UIWindow? {
        if #available(iOS 13, *) {
            return windows.first { $0.isKeyWindow }
        } else {
            return keyWindow
        }
    }

    func makeSnapshot() -> UIImage? { return getKeyWindow()?.layer.makeSnapshot() }
}


extension CALayer {
    func makeSnapshot() -> UIImage? {
        let scale = UIScreen.main.scale
        UIGraphicsBeginImageContextWithOptions(frame.size, false, scale)
        defer { UIGraphicsEndImageContext() }
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        render(in: context)
        let screenshot = UIGraphicsGetImageFromCurrentImageContext()
        return screenshot
    }
}

extension UIView {
    func makeSnapshot() -> UIImage? {
        if #available(iOS 10.0, *) {
            let renderer = UIGraphicsImageRenderer(size: frame.size)
            return renderer.image { _ in drawHierarchy(in: bounds, afterScreenUpdates: true) }
        } else {
            return layer.makeSnapshot()
        }
    }
}

extension UIImage {
    convenience init?(snapshotOf view: UIView) {
        guard let image = view.makeSnapshot(), let cgImage = image.cgImage else { return nil }
        self.init(cgImage: cgImage, scale: image.scale, orientation: image.imageOrientation)
    }
}

Verwendung

imageView.image = UIApplication.shared.makeSnapshot()

// or
imageView.image = view.makeSnapshot()

// or
imageView.image = view.layer.makeSnapshot()

// or
imageView.image = UIImage(snapshotOf: view)

Alte Lösung

Xcode 8.2.1, schnell 3

Version 1 für iOS 10x

import UIKit

extension UIApplication {

    var screenShot: UIImage?  {

        if let layer = keyWindow?.layer {
            let scale = UIScreen.main.scale

            UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
            if let context = UIGraphicsGetCurrentContext() {
                layer.render(in: context)
                let screenshot = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                return screenshot
            }
        }
        return nil
    }
}

Version 2 für iOS 9x, 10x

Wenn Sie versuchen, Code der Version 1 in iOS 9x zu verwenden , wird folgende Fehlermeldung angezeigt: CGImageCreateWithImageProvider: Ungültiger Bildanbieter: NULL.

import UIKit

extension UIApplication {

    var screenShot: UIImage?  {

        if let rootViewController = keyWindow?.rootViewController {
            let scale = UIScreen.main.scale
            let bounds = rootViewController.view.bounds
            UIGraphicsBeginImageContextWithOptions(bounds.size, false, scale);
            if let _ = UIGraphicsGetCurrentContext() {
                rootViewController.view.drawHierarchy(in: bounds, afterScreenUpdates: true)
                let screenshot = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                return screenshot
            }
        }
        return nil
    }
}

Verwendung

let screenShot = UIApplication.shared.screenShot!

1
Was ist, wenn ich das benutzerdefinierte Erscheinungsbild von AVPlayerViewController zusammen mit UIView erfassen möchte? Irgendwelche Vorschläge bitte!
Raghuram

Ich habe diesen Code ausprobiert, aber er erfasst keine Tastatur, oder?
Alexander Khitev

22

Schnelle UIImageErweiterung:

extension UIImage {

    convenience init?(view: UIView?) {
        guard let view: UIView = view else { return nil }

        UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
        guard let context: CGContext = UIGraphicsGetCurrentContext() else {
            UIGraphicsEndImageContext()
            return nil
        }

        view.layer.render(in: context)
        let contextImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        guard
            let image: UIImage = contextImage,
            let pngData: Data = image.pngData()
            else { return nil }

        self.init(data: pngData)
    }

}

Verwendung:

let myImage: UIImage? = UIImage(view: myView)

1
Das macht den Screenshot einer bestimmten Ansicht, nicht des gesamten Bildschirms.
Rodrigo Ruiz

10

Der Einfachheit halber würde ich eine Erweiterung in eine eigene Datei einfügen

import UIKit

  public extension UIWindow {

    func capture() -> UIImage {

      UIGraphicsBeginImageContextWithOptions(self.frame.size, self.opaque, UIScreen.mainScreen().scale)
      self.layer.renderInContext(UIGraphicsGetCurrentContext()!)
      let image = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()

      return image
  }
}

Rufen Sie die Nebenstelle wie folgt auf ...

let window: UIWindow! = UIApplication.sharedApplication().keyWindow

let windowImage = window.capture()

Ebenso könnte man UIView erweitern, um ein Bild davon aufzunehmen ....


5

Die empfohlene Methode zum Erstellen eines Kontexts in iOS 10 ist die Verwendung UIGraphicsImageRenderer.

extension UIView {
    func capture() -> UIImage? {
        var image: UIImage?

        if #available(iOS 10.0, *) {
            let format = UIGraphicsImageRendererFormat()
            format.opaque = isOpaque
            let renderer = UIGraphicsImageRenderer(size: frame.size, format: format)
            image = renderer.image { context in
                drawHierarchy(in: frame, afterScreenUpdates: true)
            }
        } else {
            UIGraphicsBeginImageContextWithOptions(frame.size, isOpaque, UIScreen.main.scale)
            drawHierarchy(in: frame, afterScreenUpdates: true)
            image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
        }

        return image
    }
}

Dies funktionierte sehr gut, aber im Fall einer scrollbaren Ansicht wie einer Sammlungsansicht war das resultierende Bild die erste Ansicht, nicht die tatsächliche aktuelle Ansicht.
Markhorrocks

3

Ich benutze diese Methode:

func captureScreen() -> UIImage {
    var window: UIWindow? = UIApplication.sharedApplication().keyWindow
    window = UIApplication.sharedApplication().windows[0] as? UIWindow
    UIGraphicsBeginImageContextWithOptions(window!.frame.size, window!.opaque, 0.0)
    window!.layer.renderInContext(UIGraphicsGetCurrentContext())
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image;
}

Es werden alle außer der Statusleiste erfasst und es wird nicht um Erlaubnis zum Speichern von Bildern in der Kamerarolle gebeten.

Ich hoffe es hilft!


2

Swift 3-Erweiterung für UIWindow

public extension UIWindow {

  func capture() -> UIImage? {

    UIGraphicsBeginImageContextWithOptions(self.frame.size, self.isOpaque, UIScreen.main.scale)
    self.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return image

  }
}]

2

So mache ich das in Swift 4

let layer = UIApplication.shared.keyWindow!.layer
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);                 
layer.render(in: UIGraphicsGetCurrentContext()!)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

Jetzt wird der Screenshot vom Typ UIImage sein


2

Funktioniert mit Swift 5 und iOS 13

Für alle, die eine schnelle Antwort auf eine Funktion suchen, um einen Screenshot der Ansicht als UIImage zurückzugeben:

func getScreenshot() -> UIImage? {
    //creates new image context with same size as view
    // UIGraphicsBeginImageContextWithOptions (scale=0.0) for high res capture
    UIGraphicsBeginImageContextWithOptions(view.frame.size, true, 0.0)

    // renders the view's layer into the current graphics context
    if let context = UIGraphicsGetCurrentContext() { view.layer.render(in: context) }

    // creates UIImage from what was drawn into graphics context
    let screenshot: UIImage? = UIGraphicsGetImageFromCurrentImageContext()

    // clean up newly created context and return screenshot
    UIGraphicsEndImageContext()
    return screenshot
}

Ich habe diese Antwort zusammengesetzt, indem ich den Code in der Frage übernommen und den Vorschlägen von David Rönnqvist gefolgt bin (danke für die Erklärung), mit einigen Verbesserungen.

Rufen Sie diese Methode außerhalb des Fensters anstelle der Ansicht auf, um die Navigationsleiste und andere Extras einzuschließen .

Ich brauchte einfach eine Funktion, um die Bildschirmaufnahme der Ansicht zu erhalten, also hoffe ich, dass dies jedem hilft, der nach dem gleichen sucht


1
  // Full Screen Shot function. Hope this will work well in swift.
  func screenShot() -> UIImage {                                                    
    UIGraphicsBeginImageContext(CGSizeMake(frame.size.width, frame.size.height))
    var context:CGContextRef  = UIGraphicsGetCurrentContext()
    self.view?.drawViewHierarchyInRect(frame, afterScreenUpdates: true)
    var screenShot = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext();  
    return screenShot
  }


1

Dieser Code ist die neueste Version - 100% funktioniert

func getScreenshoot() -> UIImage {
    var window: UIWindow? = UIApplication.shared.keyWindow
    window = UIApplication.shared.windows[0] as? UIWindow
    UIGraphicsBeginImageContextWithOptions(window!.frame.size, window!.isOpaque, 0.0)
    window!.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!;
}

1

Swift 5

Wenn Sie in diesem Moment nur einen echten Schnappschuss des Bildschirms (mit Tastatur und Statusleiste) benötigen:

let snap = UIScreen.main.snapshotView(afterScreenUpdates: false)

snapist ein UIViewmit einem Rahmen, der automatisch an die Grenzen des Bildschirms gesetzt wird. Wenn Sie es der Ansicht eines Ansichts-Controllers hinzufügen, erhalten Sie jetzt eine Benutzeroberfläche, die eingefroren erscheint:

view.addSubview(snap)

Keine Notwendigkeit für Erweiterungen und all das unnötige Zeug - 2 Codezeilen sind genug.


Dies funktioniert nicht, wenn Sie eine WKWebView haben, die AVPlayer anzeigt, wenn Sie beispielsweise ein MP4-Video wischen.
Zumzum


0

Meine Version erfasst auch eine Tastatur. Swift 4.2

extension UIApplication {

    var screenshot: UIImage? {
        UIGraphicsBeginImageContextWithOptions(UIScreen.main.bounds.size, false, 0)
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        for window in windows {
            window.layer.render(in: context)
        }
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }

}

Aber es erfasst nicht die Statusleiste, irgendwelche Vorschläge?
Pavan

1
@Pavan Ich wusste es nicht, weil es in meiner aktuellen App keine Statusleiste gibt.
Alexander Khitev

Ich habe es mit Beispielcode versucht, so etwas wie das Nachahmen eines Screenshots über die Power + Home-Taste, aber dieser Code erfasst keine Statusleiste.
Pavan

0

Swift 4 oder höher.

Für die Erweiterung und den Anruf von UIView, die Sie erfassen.

Erklärung

extension UIView {

    func viewCapture() -> UIImage? {

        UIGraphicsBeginImageContext(self.frame.size)

        guard let cgContext = UIGraphicsGetCurrentContext() else {
        print("Fail to get CGContext")
        return nil

    }
    self.layer.render(in: cgContext)

    guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
        print("Fail to get Image from current image context")
        return nil
    }
    UIGraphicsEndImageContext()

    return image

    }
}

Verwendung

var m_image = UIImage()

if let tempCaptureImg = self.m_Capture_View.viewCapture() {
    viewController.m_image = tempCaptureImg
}

// m_Capture_View ist ein UIView-Typ

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.