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?

cduck
sumber
Lihat saja stackoverflow.com/a/44517922/3908884
Temui Doshi

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.

Kendall Helmstetter Gelner
sumber
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
Klaas
sumber
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];
Tibidabo
sumber
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
22

iOS7 dan seterusnya, kami memiliki metode di bawah ini:

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

Memanggil metode di atas lebih cepat daripada mencoba merender isi tampilan saat ini menjadi gambar bitmap sendiri.

Jika Anda ingin menerapkan efek grafis, seperti blur, ke foto, gunakan drawViewHierarchyInRect:afterScreenUpdates:metode ini.

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

san
sumber
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)
        }
    }
}
Mike Demidov
sumber
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
Hossam Ghareeb
sumber
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.

jiwa baru
sumber
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

Matt S.
sumber
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

Bodnarchuk dengan mudah
sumber
Ini adalah contoh komprehensif. Terima kasih banyak untuk itu!
KMC
4

Apple tidak mengizinkan:

CGImageRef UIGetScreenImage();

Aplikasi harus mengambil tangkapan layar menggunakan drawRectmetode seperti yang ditentukan dalam: http://developer.apple.com/library/ios/#qa/qa2010/qa1703.html

Adeem Maqsood Basraa
sumber
2
Jadi, bagaimana dengan jawaban Matt S?
Gajendra K Chauhan
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)
anthonyqz
sumber
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
        }
    }
}
Ankit Kumar Gupta
sumber
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.

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

  return image;
}
Ayush Goel
sumber
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
Vishwas Singh
sumber