【问题标题】:How Do I Take a Screen Shot of a UIView?如何截取 UIView 的屏幕截图?
【发布时间】:2011-01-13 23:18:09
【问题描述】:

我想知道我的 iPhone 应用如何将特定UIView 的屏幕截图作为UIImage

我尝试了这段代码,但得到的只是一张空白图片。

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

myUIView 的尺寸为 320x480,并且有一些子视图。 这样做的正确方法是什么?

【问题讨论】:

标签: ios objective-c uiview screenshot


【解决方案1】:
CGImageRef UIGetScreenImage();

Apple 现在允许我们在公共应用程序中使用它,即使它是一个私有 API

【讨论】:

  • myUIView 之上还有其他我不想捕获的 UIView。否则,这会很棒。
【解决方案2】:

我想你可能想要renderInContext,而不是drawInContext。 drawInContext 更像是一种您可以覆盖的方法...

请注意,它可能不适用于所有视图,特别是大约一年前,当我尝试将它与实时相机视图一起使用时,它不起作用。

【讨论】:

【解决方案3】:
- (void)drawRect:(CGRect)rect {
  UIGraphicsBeginImageContext(self.bounds.size);    
  [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
  UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);  
}

这个方法可以放在你的Controller类中。

【讨论】:

  • drawRect 不是 UIViewController (IIRC) 的一部分。它是 UIView 的一部分。如果它在控制器中,我不相信它会被调用。
  • 如何获取保存的图片路径?
【解决方案4】:

苹果不允许:

CGImageRef UIGetScreenImage();

应用程序应使用drawRect 方法截取屏幕截图,如下所示: http://developer.apple.com/library/ios/#qa/qa2010/qa1703.html

【讨论】:

  • 那么,Matt S 的回答呢?
【解决方案5】:

您需要为屏幕截图或 UIView 捕获 键窗口。您可以使用 UIGraphicsBeginImageContextWithOptions 在 Retina Resolution 中执行此操作,并将其缩放参数设置为 0.0f。它总是以原始分辨率(iPhone 4 及更高版本的视网膜)捕获。

这个是全屏截图(关键窗口)

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

此代码以原始分辨率捕获 UIView

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

如果您需要,这会将 UIImage 以 95% 质量的 jpg 格式保存在应用的文档文件夹中。

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

【讨论】:

  • 遗憾的是,全屏截图没有捕获状态栏。不过非常不错的 sn-p。
  • 有什么办法可以截取键盘吗?
  • @tibidabo 感谢它的工作原理。但是如何保存多张图片呢?
  • “切萨皮克大内存泄漏!” - 爱马仕康拉德。 (说真的,正确管理你的 CG!)
【解决方案6】:

iOS 7 有一个新方法,允许您将视图层次结构绘制到当前图形上下文中。这可用于非常快速地获取 UIImage。

我在UIView 上实现了一个类别方法来获取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;
}

它比现有的renderInContext: 方法快得多。

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

为 SWIFT 更新:具有相同功能的扩展:

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 更新

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

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

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

【讨论】:

  • 如果你有一个大的 UILabel 或 CAShapeLayer,这是行不通的,它最终不会绘制任何东西
  • 感谢您的快速 sn-p 我解决了我的问题:stackoverflow.com/a/27764590/1139044.
  • 它解决了我的问题。我使用的是旧版本,它给了我很多错误!谢谢一百万
  • 我正在使用相同的方式截取视图的屏幕截图。如果视图将 wkwebview 作为子视图,则无法截取屏幕截图。它显示空白。如何正确截屏?
  • 在视图控制器转换期间调用它会闪烁转换结束。
【解决方案7】:

iOS7 以后,我们有以下默认方法:

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

调用上述方法比尝试自己将当前视图的内容渲染为位图图像要快。

如果您想对快照应用图形效果(例如模糊),请改用drawViewHierarchyInRect:afterScreenUpdates: 方法。

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

【讨论】:

    【解决方案8】:
    -(UIImage *)convertViewToImage
    {
        UIGraphicsBeginImageContext(self.bounds.size);
        [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
      return image;
    }
    

    【讨论】:

      【解决方案9】:

      以下sn-p用于截图:

      UIGraphicsBeginImageContext(self.muUIView.bounds.size);
      
      [myUIView.layer renderInContext:UIGraphicsGetCurrentContext()];
      
      UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
      

      使用renderInContext: 方法代替drawInContext: 方法

      renderInContext: 方法将接收器及其子层渲染到当前上下文中。这种方法 直接从层树渲染。

      【讨论】:

        【解决方案10】:

        我为 UIView 创建了可用的扩展,以便在 Swift 中截屏:

        extension UIView{
        
        var screenshot: UIImage{
        
            UIGraphicsBeginImageContext(self.bounds.size);
            let context = UIGraphicsGetCurrentContext();
            self.layer.renderInContext(context)
            let screenShot = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            return screenShot
        }
        }
        

        要使用它,只需输入:

        let screenshot = view.screenshot
        

        【讨论】:

        • 使用UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0);而不是UIGraphicsBeginImageContext(self.bounds.size);来使用设备的正确比例因子。
        • 我确认它有效,但使用 drawViewHierarchyInRect 而不是 renderInContext 无效。
        【解决方案11】:

        我从 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)
        
        }}
        

        致电

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

        如果你想创建一个png必须改变:

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

        通过

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

        【讨论】:

        • 任何想法为什么如果我截取 UITableViewCell 我得到一个空白视图,但如果我截取 tableView 我得到我期望的结果?
        • 我尝试了一个示例(UItableViewController)并且它有效,也许将您的代码放在这里以供审查
        • 诀窍是我需要使用 CGContextTranslateCTM(context, 0, -view.frame.origin.y);
        【解决方案12】:

        您可以使用以下 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
        

        【讨论】:

          【解决方案13】:

          详情

          • Xcode 版本 10.3 (10G8),Swift 5

          解决方案

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

          用法

          let image = view.makeSnapshot()
          

          完整样本

          别忘了在此处添加解决方案代码

          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>
          

          结果

          【讨论】:

          • 这是一个全面的例子。非常感谢你!
          • 这个实际上捕获了图像中的所有内容,谢谢!
          【解决方案14】:

          iOS 10 有新的 API

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

          【讨论】:

            【解决方案15】:

            Swift 4 更新:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-07-22
              • 1970-01-01
              • 1970-01-01
              • 2013-10-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多