获取UIScrollView的屏幕截图,包括屏幕外部分

Tim*_*van 55 iphone core-graphics ipad ios

我有一个UIScrollView实现takeScreenshot方法的decedent,如下所示:

-(void)takeScreenshot {  
  CGRect contextRect  = CGRectMake(0, 0, 768, 1004);
  UIGraphicsBeginImageContext(contextRect.size);    
  [self.layer renderInContext:UIGraphicsGetCurrentContext()];
  UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

  // do something with the viewImage here.
}
Run Code Online (Sandbox Code Playgroud)

这基本上移动到滚动视图的顶部,并截取可见区域的屏幕截图.当iPad面向人像时,它可以正常工作,但当它处于横向时,图像的底部会被切断(因为可见区域的高度仅为748,而不是1004).

是否有可能获得快照UIScrollView,包括不在屏幕上的区域?或者我需要向下滚动视图,拍摄第二张照片并将它们拼接在一起?

Ste*_*ntz 110

这是有效的代码......

- (IBAction) renderScrollViewToImage
{
    UIImage* image = nil;

    UIGraphicsBeginImageContext(_scrollView.contentSize);
    {
        CGPoint savedContentOffset = _scrollView.contentOffset;
        CGRect savedFrame = _scrollView.frame;

        _scrollView.contentOffset = CGPointZero;
        _scrollView.frame = CGRectMake(0, 0, _scrollView.contentSize.width, _scrollView.contentSize.height);

        [_scrollView.layer renderInContext: UIGraphicsGetCurrentContext()];     
        image = UIGraphicsGetImageFromCurrentImageContext();

        _scrollView.contentOffset = savedContentOffset;
        _scrollView.frame = savedFrame;
    }
    UIGraphicsEndImageContext();

    if (image != nil) {
        [UIImagePNGRepresentation(image) writeToFile: @"/tmp/test.png" atomically: YES];
        system("open /tmp/test.png");
    }
}
Run Code Online (Sandbox Code Playgroud)

最后几行只是将图像写入/tmp/test.png,然后在Preview.app中打开它.这显然只适用于模拟器:-)

ScrollViewScreenShot Github存储库中的完整项目

  • 在iOS 13设备中使用此方法,屏外部分是黑色的。似乎可以在以前的操作系统版本中使用,但在 iOS 13 中不行? (7认同)
  • 赞成缩进技巧,从未想过使用这样的大括号. (6认同)
  • 试试我的新答案:-) (4认同)
  • 很棒的解决方案,但我使用了UIGraphicsBeginImageContextWithOptions(_scrollView.contentSize,NO,0.0); 为了更好的分辨率 (4认同)
  • @无夜之星辰是的,我创建了视图*没有将其附加到任何超级视图*,对其进行屏幕截图。代码类似于scrollView.frame = CGRect(x: 0, y: 0, width: width, height: height),并通过创建 UIGraphicsImageRenderer(size: CGSize(width: width, height: height) 来截图视图。 (2认同)

小智 21

对我来说回答/sf/answers/247796111/没有用.我有一个在iOS 8上实现这个的任务.

实际上这种方法适用于iPhone,但iPad(simu和真实设备)是另一种情况.它只是呈现可见部分,其余图像只是空白.

我试过drawViewHierarchyInRect- 没有运气.取决于afterScreenUpdates存在truefalse我拉伸图像的一部分或再次只是图像的一部分.

因此,我发现实现正确屏幕截图的唯一方法是将scrollview添加到另一个临时视图并进行渲染.

示例代码如下(我的VC中的scrollview是插座)

func getImageOfScrollView() -> UIImage {
    var image = UIImage()

    UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, UIScreen.mainScreen().scale)

    // save initial values
    let savedContentOffset = self.scrollView.contentOffset
    let savedFrame = self.scrollView.frame
    let savedBackgroundColor = self.scrollView.backgroundColor

    // reset offset to top left point
    self.scrollView.contentOffset = CGPointZero
    // set frame to content size
    self.scrollView.frame = CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height)
    // remove background
    self.scrollView.backgroundColor = UIColor.clearColor()

    // make temp view with scroll view content size
    // a workaround for issue when image on ipad was drawn incorrectly
    let tempView = UIView(frame: CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height))

    // save superview
    let tempSuperView = self.scrollView.superview
    // remove scrollView from old superview
    self.scrollView.removeFromSuperview()
    // and add to tempView
    tempView.addSubview(self.scrollView)

    // render view
    // drawViewHierarchyInRect not working correctly
    tempView.layer.renderInContext(UIGraphicsGetCurrentContext())
    // and get image
    image = UIGraphicsGetImageFromCurrentImageContext()

    // and return everything back
    tempView.subviews[0].removeFromSuperview()
    tempSuperView?.addSubview(self.scrollView)

    // restore saved settings
    self.scrollView.contentOffset = savedContentOffset
    self.scrollView.frame = savedFrame
    self.scrollView.backgroundColor = savedBackgroundColor

    UIGraphicsEndImageContext()

    return image
}
Run Code Online (Sandbox Code Playgroud)

  • 太好了,图像已创建,但不幸的是我的滚动视图未在超级视图中恢复,有什么建议吗? (3认同)

Roo*_*tal 10

处理UIScrollView的UIView扩展的工作示例:

extension UIView {
    func screenshot() -> UIImage {

            if(self is UIScrollView) {
                let scrollView = self as! UIScrollView

                let savedContentOffset = scrollView.contentOffset
                let savedFrame = scrollView.frame

                UIGraphicsBeginImageContext(scrollView.contentSize)
                scrollView.contentOffset = .zero
                self.frame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height)
                self.layer.render(in: UIGraphicsGetCurrentContext()!)
                let image = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext();

                scrollView.contentOffset = savedContentOffset
                scrollView.frame = savedFrame

                return image!
            }

            UIGraphicsBeginImageContext(self.bounds.size)
            self.layer.render(in: UIGraphicsGetCurrentContext()!)
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return image!

        }
}
Run Code Online (Sandbox Code Playgroud)


Rya*_*anG 7

我从@Roopesh Mittal采取了上述解决方案,使其更安全/更清洁.

Swift 4兼容

fileprivate extension UIScrollView {
    func screenshot() -> UIImage? {
        let savedContentOffset = contentOffset
        let savedFrame = frame

        UIGraphicsBeginImageContext(contentSize)
        contentOffset = .zero
        frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)

        guard let context = UIGraphicsGetCurrentContext() else { return nil }

        layer.render(in: context)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext();

        contentOffset = savedContentOffset
        frame = savedFrame

        return image
    }
}
Run Code Online (Sandbox Code Playgroud)


Ice*_*loe 7

在 iOS 13 中,我遇到了这条线不起作用的问题

frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)

为了解决这个问题,我正在从父级中删除滚动视图,然后在截取屏幕截图后附加。

完整代码:

func screenshot() -> UIImage? {
        let savedContentOffset = contentOffset
        let savedFrame = frame
        defer {
            contentOffset = savedContentOffset
            frame = savedFrame
        }

        contentOffset = .zero
        frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)

        let image = UIGraphicsImageRenderer(bounds: CGRect(origin: .zero, size: contentSize)).image { renderer in
            let context = renderer.cgContext
            layer.render(in: context)
        }

        return image
    }
Run Code Online (Sandbox Code Playgroud)

获取截图:

func getScreenshot() -> UIImage? {
     scrollView.removeFromSuperview()
     let image = scrollView.screenshot()
     addScrollView()
     return image
}
Run Code Online (Sandbox Code Playgroud)


Min*_*int 6

经过改进的Swift 4.x / 5.0版本,基于@RyanG的答案

fileprivate extension UIScrollView {
    func screenshot() -> UIImage? {
        // begin image context
        UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
        // save the orginal offset & frame 
        let savedContentOffset = contentOffset
        let savedFrame = frame
        // end ctx, restore offset & frame before returning
        defer {
            UIGraphicsEndImageContext()
            contentOffset = savedContentOffset
            frame = savedFrame
        }
        // change the offset & frame so as to include all content
        contentOffset = .zero
        frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
        guard let ctx = UIGraphicsGetCurrentContext() else {
            return nil
        }
        layer.render(in: ctx)
        let image = UIGraphicsGetImageFromCurrentImageContext()

        return image
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在 iOS 13 中,这对我不起作用:( 图像是整个滚动视图的大小,但只渲染了屏幕上可见的部分 (3认同)