How Do I Take a Screen Shot of a UIView?

后端 未结 15 2574
-上瘾入骨i
-上瘾入骨i 2020-11-22 09:28

I am wondering how my iPhone app can take a screen shot of a specific UIView as a UIImage.

I tried this code but all I get is a blank image

相关标签:
15条回答
  • 2020-11-22 09:37

    Swift 4 updated :

    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
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 09:40

    iOS7 onwards, we have below default methods :

    - (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
    

    Calling above method is faster than trying to render the contents of the current view into a bitmap image yourself.

    If you want to apply a graphical effect, such as blur, to a snapshot, use the drawViewHierarchyInRect:afterScreenUpdates: method instead.

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

    0 讨论(0)
  • 2020-11-22 09:46

    Details

    • Xcode Version 10.3 (10G8), Swift 5

    Solution

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

    Usage

    let image = view.makeSnapshot()
    

    Full sample

    Do not forget to add the solution code here

    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>
    

    Result

    0 讨论(0)
  • 2020-11-22 09:47

    iOS 7 has a new method that allows you to draw a view hierarchy into the current graphics context. This can be used to get an UIImage very fast.

    I implemented a category method on UIView to get the view as an 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;
    }
    

    It is considerably faster then the existing renderInContext: method.

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

    UPDATE FOR SWIFT: An extension that does the same:

    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 FOR SWIFT 3

        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
    
        drawHierarchy(in: self.bounds, afterScreenUpdates: true)
    
        let image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return image
    
    0 讨论(0)
  • 2020-11-22 09:47

    You need to capture the key window for a screenshot or a UIView. You can do it in Retina Resolution using UIGraphicsBeginImageContextWithOptions and set its scale parameter 0.0f. It always captures in native resolution (retina for iPhone 4 and later).

    This one does a full screen screenshot (key window)

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

    This code capture a UIView in native resolution

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

    This saves the UIImage in jpg format with 95% quality in the app's document folder if you need to do that.

    NSString  *imagePath = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"Documents/capturedImage.jpg"]];    
    [UIImageJPEGRepresentation(capturedImage, 0.95) writeToFile:imagePath atomically:YES];
    
    0 讨论(0)
  • 2020-11-22 09:49
    - (void)drawRect:(CGRect)rect {
      UIGraphicsBeginImageContext(self.bounds.size);    
      [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
      UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
      UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);  
    }
    

    This method may put in your Controller class.

    0 讨论(0)
提交回复
热议问题