Bir UIView'in Ekran Görüntüsü Nasıl Alınır?


133

Benim iPhone uygulaması spesifik bir ekran görüntüsünü alabilir nasıl merak ediyorum UIViewbir şekilde UIImage.

Bu kodu denedim ama tüm elde boş bir görüntü.

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

myUIView320x480 boyutlarında ve bazı alt görünümleri var. Bunu yapmanın doğru yolu nedir?


Yanıtlar:


73

Bence isteyebilirsiniz renderInContext, değil drawInContext. drawInContext daha geçersiz kılmak bir yöntemdir ...

Tüm kameralarda, özellikle de canlı kamera görüntüsü ile kullanmaya çalıştığımda bir yıl kadar önce çalışmayabileceğini unutmayın.


Merhaba Kendall Bir UIView içeriğini sabit görüntü olarak değil, video olarak yakalamak için tavsiyeniz var mı? Zaman ayırdığınız için teşekkürler! Soru burada: stackoverflow.com/questions/34956713/…
Crashalot

187

iOS 7'de mevcut grafik bağlamına bir görünüm hiyerarşisi çizmenize izin veren yeni bir yöntem var. Bu, bir UIImage'ı çok hızlı almak için kullanılabilir.

UIViewGörünümü aşağıdaki gibi almak için bir kategori yöntemi uyguladım 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;
}

Mevcut renderInContext:yöntemden çok daha hızlıdır .

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

SWIFT İÇİN GÜNCELLEME : Aynı şeyi yapan bir uzantı:

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

SWIFT 3 İÇİN GÜNCELLEME

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

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

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

Büyük bir UILabel veya CAShapeLayer'ınız varsa, bu işe yaramaz, hiçbir şey
çizmez

hızlı snippet'iniz sayesinde sorunumu çözdüm: stackoverflow.com/a/27764590/1139044 .
Nicholas

sorunumu çözdü. Eski sürümü kullanıyordum ve bana bir sürü hata veriyordu! Bir milyon teşekkürler
apinho

Aynı görüntüyü ekran görüntüsü almak için de kullanıyorum. Bir görünümde alt görünüm olarak wkwebview varsa, ekran görüntüsünü alamaz. Boş gösterir. Ekran görüntüsü nasıl düzgün alınır?
Rikesh Subedi

1
Bir görünüm denetleyicisi geçişi sırasında bu çağrıldığında geçişin sonu yanıp söner.
Iulian Onofrei

63

Ekran görüntüsü veya UIView için anahtar pencereyi yakalamanız gerekir . Retina özünürlüğü içinde UIGraphicsBeginImageContextWithOptions kullanarak yapabilir ve ölçek parametresini 0.0f olarak ayarlayabilirsiniz. Her zaman doğal çözünürlükte yakalar (iPhone 4 ve üstü için retina).

Bu, tam ekran ekran görüntüsü (anahtar penceresi) yapar

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

Bu kod yerel çözünürlükte bir UIView yakalar

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

Bu, eğer yapmanız gerekiyorsa, uygulamanın belge klasöründe UIImage'ı% 95 kalitede jpg formatında kaydeder.

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

Tam ekran ekran görüntüsü ne yazık ki durum çubuğunu yakalamıyor. Çok güzel snippet olsa.
neoneye

Klavyeyi yakalamanın herhangi bir yolu var mı?
24v14

@tibidabo teşekkürler çalışıyor. Ancak birden fazla görüntüyü nasıl kaydedebilirim?
josef

İnternethaber.com "Chesapeake'nin Büyük Hafıza Sızıntısı!" - Hermes Conrad. (Cidden, CG'nizi düzgün bir şekilde yönetin !!)
Albert Renshaw

22

iOS7'den sonra, aşağıdaki varsayılan yöntemlere sahibiz:

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

Yukarıdaki yöntemi çağırmak, geçerli görünümün içeriğini bir bitmap görüntüsüne dönüştürmeye çalışmaktan daha hızlıdır.

Anlık görüntüye bulanıklık gibi bir grafik efekti uygulamak istiyorsanız, drawViewHierarchyInRect:afterScreenUpdates:bunun yerine yöntemi kullanın.

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


13

İOS 10'dan yeni API var

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

10

Swift'te ekran görüntüsü almak için UIView için kullanılabilir bir uzantı oluşturdum:

extension UIView{

var screenshot: UIImage{

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

Kullanmak için şunu yazın:

let screenshot = view.screenshot

1
Cihazın doğru ölçek faktörünü kullanmak UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0);yerine kullanın UIGraphicsBeginImageContext(self.bounds.size);.
knshn

1
İşe yaradığını onaylıyorum, ama kullanmak drawViewHierarchyInRectyerine kullanmıyor renderInContext .
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);  
}

Bu yöntem Controller sınıfınıza girebilir.


2
drawRectolduğu değil UIViewController (IIRC) bir parçasıdır. Bu bir UIView'in bir parçasıdır. Eğer kontrolörde çağrılacak inanmıyorum.
jww

Kaydedilen görüntü yolunu nasıl alabilirim?
GameDevGuru

5
CGImageRef UIGetScreenImage();

Apple artık özel bir API olmasına rağmen herkese açık bir uygulamada kullanmamıza izin veriyor


MyUIView uygulamasının üzerinde yakalamak istemediğim başka UIView'ler var. Aksi takdirde, bu harika olurdu.
cduck

5

ayrıntılar

  • Xcode Sürüm 10.3 (10G8), Swift 5

Çözüm

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

kullanım

let image = view.makeSnapshot()

Tam örnek

Çözüm kodunu buraya eklemeyi unutmayın

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>

Sonuç

resim açıklamasını buraya girin resim açıklamasını buraya girin


Bu kapsamlı bir örnek. Bunun için çok teşekkür ederim!
KMC


4

Bu uzantıyı UIView'dan ekran görüntüsü kaydetmek için oluşturdum

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)

}}

çağrı :

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

Bir png oluşturmak istiyorsanız şunu değiştirmeniz gerekir:

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

tarafından

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

Herhangi bir fikir neden bir UITableViewCell ekran görüntüsü eğer boş bir görünüm olsun, ama tableView ekran görüntüsü varsa ben beklediğim ne olsun?
21'te Unome

Bir örnek (UItableViewController) ile denedim ve işe yarıyor, belki kodunuzu inceleme için buraya koyun
anthonyqz

Hile, bir CGContextTranslateCTM (context, 0, -view.frame.origin.y) kullanmam gerektiğiydi;
23'te Unome

3

Swift 4 güncellendi:

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

Bu ekran görüntüsü yöntemi harika çalıştı.
eonist

2

Ekran görüntüsü almak için aşağıdaki snippet kullanılır:

UIGraphicsBeginImageContext(self.muUIView.bounds.size);

[myUIView.layer renderInContext:UIGraphicsGetCurrentContext()];

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

Kullanım renderInContext:yerine yöntemi drawInContext:yöntemiyle

renderInContext:yöntemi, alıcıyı ve alt katmanlarını geçerli bağlama dönüştürür. Bu yöntem doğrudan katman ağacından oluşturulur.


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

  return image;
}

0

aşağıdaki UIView kategorisini kullanabilirsiniz -

@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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.