¿Cómo hago una captura de pantalla de una UIView?

133

Me pregunto cómo mi aplicación de iPhone puede tomar una captura de pantalla de un específico UIViewcomo UIImage.

Intenté este código pero todo lo que obtengo es una imagen en blanco.

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

myUIViewtiene dimensiones 320x480 y tiene algunas sub-vistas. ¿Cuál es la forma correcta de hacer esto?

cduck
fuente
Solo échale un vistazo stackoverflow.com/a/44517922/3908884
Conoce a Doshi el

Respuestas:

73

Creo que puede querer renderInContext, no drawInContext. drawInContext es más un método que anularía ...

Tenga en cuenta que es posible que no funcione en todas las vistas, específicamente hace aproximadamente un año, cuando traté de usar esto con la vista de cámara en vivo, no funcionó.

Kendall Helmstetter Gelner
fuente
Hola Kendall, ¿tienes algún consejo para capturar el contenido de una UIView no como una imagen fija, sino como un video? ¡Gracias por tu tiempo! Pregunta aquí: stackoverflow.com/questions/34956713/…
Crashalot
187

iOS 7 tiene un nuevo método que le permite dibujar una jerarquía de vistas en el contexto gráfico actual. Esto se puede usar para obtener un UIImage muy rápido.

Implementé un método de categoría UIViewpara obtener la vista como 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 considerablemente más rápido que el renderInContext:método existente .

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

ACTUALIZACIÓN PARA SWIFT : una extensión que hace lo mismo:

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

ACTUALIZACIÓN PARA SWIFT 3

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

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

    let image = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return image
Klaas
fuente
Si tiene un UILabel o CAShapeLayer grande, esto no funciona, termina sin dibujar nada
jjxtra
Gracias a su fragmento rápido resolví mi problema: stackoverflow.com/a/27764590/1139044 .
Nicholas
resolvió mi problema Estaba usando la versión anterior y me daba muchos errores. Un millón de gracias
apinho
Estoy usando la misma forma para tomar una captura de pantalla de una vista. Si una vista tiene wkwebview como subvista, no puede tomar la captura de pantalla. Se muestra en blanco. ¿Cómo tomar la captura de pantalla correctamente?
Rikesh Subedi
1
Llamar a esto durante una transición de controladores de vista parpadea al final de la transición.
Iulian Onofrei
63

Debe capturar la ventana clave para una captura de pantalla o una vista UIView. Puedes hacerlo en Retina Resolution utilizando UIGraphicsBeginImageContextWithOptions y establecer su parámetro de escala 0.0f. Siempre captura en resolución nativa (retina para iPhone 4 y posterior).

Este hace una captura de pantalla completa (ventana clave)

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

Este código captura una vista UIView en resolución nativa

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

Esto guarda el UIImage en formato jpg con un 95% de calidad en la carpeta de documentos de la aplicación si necesita hacerlo.

NSString  *imagePath = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"Documents/capturedImage.jpg"]];    
[UIImageJPEGRepresentation(capturedImage, 0.95) writeToFile:imagePath atomically:YES];
Tibidabo
fuente
La captura de pantalla completa lamentablemente no captura la barra de estado. Muy buen fragmento sin embargo.
neoneye
¿Hay alguna forma de capturar el teclado?
mrvincenzo
@tibidabo gracias, funciona. Pero, ¿cómo puedo guardar más de una imagen?
josef
"Gran fuga de memoria de Chesapeake!" - Hermes Conrad. (¡En serio, gestiona tu CG correctamente!)
Albert Renshaw
22

iOS7 en adelante, tenemos los siguientes métodos predeterminados:

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

Llamar al método anterior es más rápido que intentar representar el contenido de la vista actual en una imagen de mapa de bits usted mismo.

Si desea aplicar un efecto gráfico, como desenfoque, a una instantánea, utilice el drawViewHierarchyInRect:afterScreenUpdates:método en su lugar.

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

san
fuente
13

Hay una nueva API de 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
fuente
10

He creado una extensión utilizable para que UIView tome una captura de pantalla en Swift:

extension UIView{

var screenshot: UIImage{

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

Para usarlo simplemente escriba:

let screenshot = view.screenshot
Hossam Ghareeb
fuente
1
Use en UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0);lugar de UIGraphicsBeginImageContext(self.bounds.size);usar el factor de escala correcto del dispositivo.
knshn
1
Confirmo que funciona, pero usar en drawViewHierarchyInRectlugar de renderInContext no.
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);  
}

Este método puede poner en su clase de controlador.

alma nueva
fuente
2
drawRectes no parte de la UIViewController (IIRC). Es parte de una UIView. No creo que se llame si está en el controlador.
jww
¿Cómo puedo obtener la ruta de la imagen guardada?
GameDevGuru
5
CGImageRef UIGetScreenImage();

Apple ahora nos permite usarlo en una aplicación pública, aunque sea una API privada

Matt S.
fuente
Hay otras UIViews encima de myUIView que no quiero capturar. De lo contrario, esto sería genial.
cduck
5

Detalles

  • Xcode versión 10.3 (10G8), Swift 5

Solución

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

Uso

let image = view.makeSnapshot()

Muestra completa

No olvides agregar el código de la solución aquí

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>

Resultado

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Vasily Bodnarchuk
fuente
Este es un ejemplo completo. ¡Muchas gracias por eso!
KMC
4

Creé esta extensión para guardar una captura de pantalla de UIView

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)

}}

llamada :

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

Si quieres crear un png debes cambiar:

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

por

UIImagePNGRepresentation(image)?.writeToFile(path, atomically: true)
anthonyqz
fuente
¿Alguna idea de por qué si tomo una captura de pantalla de UITableViewCell obtengo una vista en blanco, pero si tomo una captura de pantalla de tableView obtengo lo que espero?
Unome
Intenté con un ejemplo (UItableViewController) y funciona, tal vez ponga su código aquí para su revisión
anthonyqz
El truco fue que necesitaba usar un CGContextTranslateCTM (contexto, 0, -view.frame.origin.y);
Unome
3

Swift 4 actualizado:

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
fuente
Este método de captura de pantalla funcionó muy bien.
eonista
2

El siguiente fragmento se utiliza para tomar capturas de pantalla:

UIGraphicsBeginImageContext(self.muUIView.bounds.size);

[myUIView.layer renderInContext:UIGraphicsGetCurrentContext()];

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

Use el renderInContext:método en lugar del drawInContext:método

renderInContext:El método representa el receptor y sus subcapas en el contexto actual. Este método se procesa directamente desde el árbol de capas.

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

  return image;
}
Ayush Goel
fuente
0

puede usar la siguiente categoría de UIView:

@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
fuente