Bagaimana Saya Mengambil Cuplikan Layar dari UIView?


133

Saya bertanya-tanya bagaimana aplikasi iPhone saya dapat mengambil screenshot tertentu UIViewsebagai UIImage.

Saya mencoba kode ini tetapi yang saya dapatkan hanyalah gambar kosong.

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

myUIViewmemiliki dimensi 320x480 dan memiliki beberapa sub-tampilan. Apa cara yang benar untuk melakukan ini?


Jawaban:


73

Saya pikir Anda mungkin mau renderInContext, tidak drawInContext. drawInContext lebih merupakan metode yang Anda timpa ...

Perhatikan bahwa ini mungkin tidak berfungsi di semua tampilan, khususnya setahun atau lebih yang lalu ketika saya mencoba menggunakan ini dengan tampilan kamera langsung, itu tidak berfungsi.


Hai Kendall, apakah Anda memiliki saran untuk mengambil konten UIView bukan sebagai gambar diam, tetapi sebagai video? Terima kasih atas waktunya! Pertanyaan di sini: stackoverflow.com/questions/34956713/…
Crashalot

187

iOS 7 memiliki metode baru yang memungkinkan Anda untuk menarik hierarki tampilan ke dalam konteks grafik saat ini. Ini dapat digunakan untuk mendapatkan UIImage dengan sangat cepat.

Saya menerapkan metode kategori UIViewuntuk mendapatkan tampilan sebagai 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;
}

Ini jauh lebih cepat daripada renderInContext:metode yang ada .

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

PEMBARUAN UNTUK SWIFT : Ekstensi yang melakukan hal yang sama:

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

PEMBARUAN UNTUK SWIFT 3

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

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

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

Jika Anda memiliki UILabel atau CAShapeLayer yang besar, ini tidak berfungsi, akhirnya tidak menggambar apa pun
jjxtra

terima kasih kepada cuplikan cepat Anda, saya memecahkan masalah saya: stackoverflow.com/a/27764590/1139044 .
Nicholas

itu memecahkan masalah saya. Saya menggunakan versi lama dan itu memberi saya banyak kesalahan! Terima kasih satu juta
apinho

Saya menggunakan cara yang sama untuk mengambil screenshot tampilan. Jika suatu tampilan memiliki wkwebview sebagai subview, itu tidak dapat mengambil screenshot. Itu menunjukkan kosong. Bagaimana cara mengambil screenshot dengan benar?
Rikesh Subedi

1
Memanggil ini selama transisi pengontrol tampilan berkedip akhir transisi.
Iulian Onofrei

63

Anda perlu menangkap jendela kunci untuk tangkapan layar atau UIView. Anda dapat melakukannya dalam Resolusi Retina menggunakan UIGraphicsBeginImageContextWithOptions dan mengatur parameter skalanya 0,0f. Selalu menangkap dalam resolusi asli (retina untuk iPhone 4 dan yang lebih baru).

Yang ini melakukan tangkapan layar layar penuh (jendela kunci)

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

Kode ini menangkap UIView dalam resolusi asli

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

Ini menyimpan UIImage dalam format jpg dengan kualitas 95% di folder dokumen aplikasi jika Anda perlu melakukannya.

NSString  *imagePath = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"Documents/capturedImage.jpg"]];    
[UIImageJPEGRepresentation(capturedImage, 0.95) writeToFile:imagePath atomically:YES];

Tangkapan layar layar penuh sayangnya tidak menangkap bilah status. Cuplikan yang sangat bagus.
neoneye

Apakah ada cara untuk menangkap keyboard?
mrvincenzo

@tibidabo terima kasih itu berfungsi. Tetapi bagaimana saya bisa menyimpan lebih dari satu gambar?
josef

"Kebocoran Hebat Chesapeake!" - Hermes Conrad. (Meskipun serius, kelola CG Anda dengan benar !!)
Albert Renshaw


13

Ada API baru dari iOS 10

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

10

Saya telah membuat ekstensi yang dapat digunakan untuk UIView untuk mengambil tangkapan layar di Swift:

extension UIView{

var screenshot: UIImage{

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

Untuk menggunakannya cukup ketik:

let screenshot = view.screenshot

1
Gunakan UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0);daripada UIGraphicsBeginImageContext(self.bounds.size);menggunakan faktor skala yang tepat pada perangkat.
knshn

1
Saya mengonfirmasi itu berfungsi, tetapi menggunakan drawViewHierarchyInRectbukannya renderInContext tidak.
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);  
}

Metode ini dapat dimasukkan ke dalam kelas Controller Anda.


2
drawRectadalah tidak bagian dari UIViewController (IIRC). Itu adalah bagian dari UIView. Saya tidak percaya itu akan dipanggil jika ada di controller.
jww

Bagaimana saya bisa mendapatkan jalur gambar yang disimpan?
GameDevGuru

5
CGImageRef UIGetScreenImage();

Apple sekarang memungkinkan kita untuk menggunakannya dalam aplikasi publik, meskipun itu adalah API pribadi


Ada UIViews lain di atas myIView yang tidak ingin saya tangkap. Kalau tidak, ini akan bagus.
cduck

5

Detail

  • Xcode Versi 10.3 (10G8), Swift 5

Larutan

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

Pemakaian

let image = view.makeSnapshot()

Sampel lengkap

Jangan lupa untuk menambahkan kode solusi di sini

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>

Hasil

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini


Ini adalah contoh komprehensif. Terima kasih banyak untuk itu!
KMC


4

Saya membuat ekstensi ini untuk menyimpan tangkapan layar dari 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)

}}

hubungi :

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

Jika Anda ingin membuat png harus berubah:

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

oleh

UIImagePNGRepresentation(image)?.writeToFile(path, atomically: true)

Ada ide mengapa jika saya screenshot UITableViewCell saya mendapatkan tampilan kosong, tetapi jika saya screenshot tableView saya mendapatkan apa yang saya harapkan?
Unome

Saya mencoba dengan sebuah contoh (UItableViewController) dan Berhasil, mungkin meletakkan kode Anda di sini untuk ditinjau
anthonyqz

Triknya adalah bahwa saya perlu menggunakan CGContextTranslateCTM (konteks, 0, -view.frame.origin.y);
Unome

3

Swift 4 diperbarui:

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

Metode tangkapan layar ini bekerja sangat baik.
eonist

2

Cuplikan berikut digunakan untuk mengambil tangkapan layar:

UIGraphicsBeginImageContext(self.muUIView.bounds.size);

[myUIView.layer renderInContext:UIGraphicsGetCurrentContext()];

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

Gunakan renderInContext:metode, bukan drawInContext:metode

renderInContext:metode menjadikan penerima dan sublapisannya dalam konteks saat ini. Metode ini dirender langsung dari pohon lapisan.


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

  return image;
}

0

Anda dapat menggunakan kategori UIView berikut -

@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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.