Wie mache ich einen Screenshot eines UIView?

133

Ich frage mich, wie meine iPhone-App einen Screenshot eines bestimmten UIViewals erstellen kann UIImage.

Ich habe diesen Code ausprobiert, aber alles, was ich bekomme, ist ein leeres Bild.

UIGraphicsBeginImageContext(CGSizeMake(320,480));
CGContextRef context = UIGraphicsGetCurrentContext();
[myUIView.layer drawInContext:context];
UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

myUIViewhat die Abmessungen 320x480 und einige Unteransichten. Was ist der richtige Weg, um dies zu tun?

Ente
quelle
Schauen
Treffen Sie Doshi am

Antworten:

73

Ich denke, Sie möchten vielleicht renderInContextnicht drawInContext. drawInContext ist eher eine Methode, die Sie überschreiben würden ...

Beachten Sie, dass es möglicherweise nicht in allen Ansichten funktioniert, insbesondere vor einem Jahr oder so, als ich versuchte, dies mit der Live-Kameraansicht zu verwenden, funktionierte es nicht.

Kendall Helmstetter Gelner
quelle
Hallo Kendall, haben Sie Ratschläge, wie Sie den Inhalt eines UIView nicht als Standbild, sondern als Video erfassen können? Vielen Dank für Ihre Zeit! Frage hier: stackoverflow.com/questions/34956713/…
Crashalot
187

iOS 7 verfügt über eine neue Methode, mit der Sie eine Ansichtshierarchie in den aktuellen Grafikkontext zeichnen können. Dies kann verwendet werden, um ein UIImage sehr schnell zu erhalten.

Ich habe eine Kategoriemethode implementiert UIView, um die Ansicht als UIImage:

- (UIImage *)pb_takeSnapshot {
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);

    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];

    // old style [self.layer renderInContext:UIGraphicsGetCurrentContext()];

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

Es ist erheblich schneller als die bestehende renderInContext:Methode.

Referenz: https://developer.apple.com/library/content/qa/qa1817/_index.html

UPDATE FÜR SWIFT : Eine Erweiterung, die dasselbe tut:

extension UIView {

    func pb_takeSnapshot() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)

        drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)

        // old style: layer.renderInContext(UIGraphicsGetCurrentContext())

        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

UPDATE FÜR SWIFT 3

    UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)

    drawHierarchy(in: self.bounds, afterScreenUpdates: true)

    let image = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return image
Klaas
quelle
Wenn Sie ein großes UILabel oder CAShapeLayer haben, funktioniert dies nicht, es wird nichts gezeichnet
jjxtra
Dank Ihres schnellen Snippets habe ich mein Problem gelöst: stackoverflow.com/a/27764590/1139044 .
Nicholas
es hat mein Problem gelöst. Ich habe die alte Version verwendet und es gab mir viele Fehler! Vielen Dank eine Million
Apinho
Ich verwende den gleichen Weg, um einen Screenshot einer Ansicht zu machen. Wenn eine Ansicht wkwebview als Unteransicht hat, kann sie keinen Screenshot machen. Es wird leer angezeigt. Wie mache ich einen Screenshot richtig?
Rikesh Subedi
1
Wenn Sie dies während eines View Controller-Übergangs aufrufen, wird das Ende des Übergangs angezeigt.
Iulian Onofrei
63

Sie müssen das Schlüsselfenster für einen Screenshot oder eine UIView erfassen. Sie können dies in der Retina-Auflösung mit UIGraphicsBeginImageContextWithOptions tun und den Skalierungsparameter 0.0f festlegen. Es wird immer in nativer Auflösung erfasst (Retina für iPhone 4 und höher).

Dieser macht einen Vollbild-Screenshot (Schlüsselfenster)

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
CGRect rect = [keyWindow bounds];
UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[keyWindow.layer renderInContext:context];   
UIImage *capturedScreen = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Dieser Code erfasst eine UIView in nativer Auflösung

CGRect rect = [captureView bounds];
UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[captureView.layer renderInContext:context];   
UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Dadurch wird das UIImage im JPG-Format mit 95% Qualität im Dokumentordner der App gespeichert, falls dies erforderlich ist.

NSString  *imagePath = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"Documents/capturedImage.jpg"]];    
[UIImageJPEGRepresentation(capturedImage, 0.95) writeToFile:imagePath atomically:YES];
Tibidabo
quelle
Der Vollbild-Screenshot erfasst leider nicht die Statusleiste. Sehr schöner Ausschnitt.
Neoneye
Gibt es eine Möglichkeit, die Tastatur zu erfassen?
Mrvincenzo
@ Tibidabo danke es funktioniert. Aber wie kann ich mehr als ein Bild speichern?
Josef
"Tolles Erinnerungsleck von Chesapeake!" - Hermes Conrad. (Im Ernst, verwalten Sie Ihre CG richtig !!)
Albert Renshaw
22

Ab iOS7 haben wir folgende Standardmethoden:

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

Das Aufrufen der obigen Methode ist schneller als der Versuch, den Inhalt der aktuellen Ansicht selbst in ein Bitmap-Bild zu rendern.

Wenn Sie einen grafischen Effekt wie z. B. Unschärfe auf einen Schnappschuss anwenden möchten, verwenden Sie drawViewHierarchyInRect:afterScreenUpdates:stattdessen die Methode.

https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/uiview/uiview.html

san
quelle
13

Es gibt eine neue API von iOS 10

extension UIView {
    func makeScreenshot() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: self.bounds)
        return renderer.image { (context) in
            self.layer.render(in: context.cgContext)
        }
    }
}
Mike Demidov
quelle
10

Ich habe eine verwendbare Erweiterung für UIView erstellt, um Screenshots in Swift aufzunehmen:

extension UIView{

var screenshot: UIImage{

    UIGraphicsBeginImageContext(self.bounds.size);
    let context = UIGraphicsGetCurrentContext();
    self.layer.renderInContext(context)
    let screenShot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return screenShot
}
}

Um es zu benutzen, geben Sie einfach Folgendes ein:

let screenshot = view.screenshot
Hossam Ghareeb
quelle
1
Verwenden Sie den richtigen Skalierungsfaktor des Geräts, UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0);anstatt ihn UIGraphicsBeginImageContext(self.bounds.size);zu verwenden.
Knshn
1
Ich bestätige, dass es funktioniert, aber drawViewHierarchyInRectanstelle von renderInContext nicht.
Mike Demidov
7
- (void)drawRect:(CGRect)rect {
  UIGraphicsBeginImageContext(self.bounds.size);    
  [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
  UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);  
}

Diese Methode kann in Ihre Controller-Klasse eingefügt werden.

neue Seele
quelle
2
drawRectist nicht Teil des UIViewController (IIRC). Es ist Teil eines UIView. Ich glaube nicht, dass es aufgerufen wird, wenn es im Controller ist.
JWW
Wie kann ich den gespeicherten Bildpfad abrufen?
GameDevGuru
5
CGImageRef UIGetScreenImage();

Apple erlaubt uns jetzt, es in einer öffentlichen Anwendung zu verwenden, obwohl es sich um eine private API handelt

Matt S.
quelle
Über myUIView befinden sich weitere UIViews, die ich nicht erfassen möchte. Sonst wäre das toll.
Cduck
5

Einzelheiten

  • Xcode Version 10.3 (10G8), Swift 5

Lösung

import UIKit

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

Verwendung

let image = view.makeSnapshot()

Vollständige Probe

Vergessen Sie nicht, hier den Lösungscode hinzuzufügen

import UIKit

class ViewController: UIViewController {

    @IBOutlet var viewForScreenShot: UIView!
    @IBOutlet var screenShotRenderer: UIImageView!

    @IBAction func makeViewScreenShotButtonTapped2(_ sender: UIButton) {
        screenShotRenderer.image = viewForScreenShot.makeSnapshot()
    }
}

Main.storyboard

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="stackoverflow_2214957" customModuleProvider="target" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Acg-GO-mMN">
                                <rect key="frame" x="67" y="28" width="240" height="128"/>
                                <subviews>
                                    <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="4Fr-O3-56t">
                                        <rect key="frame" x="72" y="49" width="96" height="30"/>
                                        <constraints>
                                            <constraint firstAttribute="height" constant="30" id="cLv-es-h7Q"/>
                                            <constraint firstAttribute="width" constant="96" id="ytF-FH-gdm"/>
                                        </constraints>
                                        <nil key="textColor"/>
                                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                        <textInputTraits key="textInputTraits"/>
                                    </textField>
                                </subviews>
                                <color key="backgroundColor" red="0.0" green="0.47843137250000001" blue="1" alpha="0.49277611300000002" colorSpace="custom" customColorSpace="sRGB"/>
                                <color key="tintColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
                                <constraints>
                                    <constraint firstItem="4Fr-O3-56t" firstAttribute="centerX" secondItem="Acg-GO-mMN" secondAttribute="centerX" id="egj-rT-Gz5"/>
                                    <constraint firstItem="4Fr-O3-56t" firstAttribute="centerY" secondItem="Acg-GO-mMN" secondAttribute="centerY" id="ymi-Ll-WIV"/>
                                </constraints>
                            </view>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SQq-IE-pvj">
                                <rect key="frame" x="109" y="214" width="157" height="30"/>
                                <state key="normal" title="make view screen shot"/>
                                <connections>
                                    <action selector="makeViewScreenShotButtonTapped2:" destination="BYZ-38-t0r" eventType="touchUpInside" id="KSY-ec-uvA"/>
                                </connections>
                            </button>
                            <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="CEZ-Ju-Tpq">
                                <rect key="frame" x="67" y="269" width="240" height="128"/>
                                <constraints>
                                    <constraint firstAttribute="width" constant="240" id="STo-iJ-rM4"/>
                                    <constraint firstAttribute="height" constant="128" id="tfi-zF-zdn"/>
                                </constraints>
                            </imageView>
                        </subviews>
                        <color key="backgroundColor" red="0.95941069162436543" green="0.95941069162436543" blue="0.95941069162436543" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="CEZ-Ju-Tpq" firstAttribute="top" secondItem="SQq-IE-pvj" secondAttribute="bottom" constant="25" id="6x1-iB-gKF"/>
                            <constraint firstItem="Acg-GO-mMN" firstAttribute="leading" secondItem="CEZ-Ju-Tpq" secondAttribute="leading" id="LUp-Be-FiC"/>
                            <constraint firstItem="SQq-IE-pvj" firstAttribute="top" secondItem="Acg-GO-mMN" secondAttribute="bottom" constant="58" id="Qu0-YT-k9O"/>
                            <constraint firstItem="Acg-GO-mMN" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Qze-zd-ajY"/>
                            <constraint firstItem="Acg-GO-mMN" firstAttribute="trailing" secondItem="CEZ-Ju-Tpq" secondAttribute="trailing" id="b1d-sp-GHD"/>
                            <constraint firstItem="SQq-IE-pvj" firstAttribute="centerX" secondItem="CEZ-Ju-Tpq" secondAttribute="centerX" id="qCL-AF-Cro"/>
                            <constraint firstItem="Acg-GO-mMN" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" constant="8" symbolic="YES" id="u5Y-eh-oSG"/>
                            <constraint firstItem="CEZ-Ju-Tpq" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="vkx-JQ-pOF"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="screenShotRenderer" destination="CEZ-Ju-Tpq" id="8QB-OE-ib6"/>
                        <outlet property="viewForScreenShot" destination="Acg-GO-mMN" id="jgL-yn-8kk"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="32.799999999999997" y="37.331334332833585"/>
        </scene>
    </scenes>
</document>

Ergebnis

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Wassili Bodnarchuk
quelle
Dies ist ein umfassendes Beispiel. Vielen Dank dafür!
KMC
4

Ich habe diese Erweiterung erstellt, um einen Screenshot von UIView zu speichern

extension UIView {
func saveImageFromView(path path:String) {
    UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)
    drawViewHierarchyInRect(bounds, afterScreenUpdates: true)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    UIImageJPEGRepresentation(image, 0.4)?.writeToFile(path, atomically: true)

}}

Anruf :

let pathDocuments = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).first!
let pathImage = "\(pathDocuments)/\(user!.usuarioID.integerValue).jpg"
reportView.saveImageFromView(path: pathImage)

Wenn Sie ein PNG erstellen möchten, muss sich Folgendes ändern:

UIImageJPEGRepresentation(image, 0.4)?.writeToFile(path, atomically: true)

durch

UIImagePNGRepresentation(image)?.writeToFile(path, atomically: true)
anthonyqz
quelle
Irgendwelche Ideen, warum ich beim Screenshot einer UITableViewCell eine leere Ansicht bekomme, aber wenn ich die tableView scanne, bekomme ich das, was ich erwarte?
Unome
Ich habe es mit einem Beispiel versucht (UItableViewController) und es funktioniert, vielleicht setzen Sie Ihren Code hier zur Überprüfung
anthonyqz
Der Trick war, dass ich ein CGContextTranslateCTM (context, 0, -view.frame.origin.y) verwenden musste;
Unome
3

Swift 4 aktualisiert:

extension UIView {
   var screenShot: UIImage?  {
        if #available(iOS 10, *) {
            let renderer = UIGraphicsImageRenderer(bounds: self.bounds)
            return renderer.image { (context) in
                self.layer.render(in: context.cgContext)
            }
        } else {
            UIGraphicsBeginImageContextWithOptions(bounds.size, false, 5);
            if let _ = UIGraphicsGetCurrentContext() {
                drawHierarchy(in: bounds, afterScreenUpdates: true)
                let screenshot = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                return screenshot
            }
            return nil
        }
    }
}
Ankit Kumar Gupta
quelle
Diese Screenshot-Methode hat super funktioniert.
Eonist
2

Das folgende Snippet wird verwendet, um einen Screenshot aufzunehmen:

UIGraphicsBeginImageContext(self.muUIView.bounds.size);

[myUIView.layer renderInContext:UIGraphicsGetCurrentContext()];

UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Verwenden Sie renderInContext:Methode anstelle von drawInContext:Methode

renderInContext:Methode macht den Empfänger und seine Unterschichten in den aktuellen Kontext. Diese Methode wird direkt aus dem Ebenenbaum gerendert.

Jayprakash Dubey
quelle
1
-(UIImage *)convertViewToImage
{
    UIGraphicsBeginImageContext(self.bounds.size);
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

  return image;
}
Ayush Goel
quelle
0

Sie können folgende UIView-Kategorie verwenden -

@implementation UIView (SnapShot)

 - (UIImage *)snapshotImage
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);        
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];        
    // old style [self.layer renderInContext:UIGraphicsGetCurrentContext()];        
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();        
    UIGraphicsEndImageContext();        
    return image;
}    
@end
Vishwas Singh
quelle