Getting a screenshot of a UIScrollView, including offscreen parts
Asked Answered
M

16

59

I have a UIScrollView decendent that implements a takeScreenshot method that looks like this:

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

This basically moves to the top of the scroll view, and takes a screenshot of the visible area. It works fine when the iPad is oriented portrait, but when it's in landscape the bottom of the image is cut off (as the height of the visible area is only 748, not 1004).

Is it possible to get a snapshot of the UIScrollView, including areas not on screen? Or do I need to scroll the view down, take a second photo and stitch them together?

Mensa answered 22/8, 2010 at 1:28 Comment(0)
D
113

Here is code that works ...

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

The last few lines simply write the image to /tmp/test.png and then opens it in Preview.app. This obviously only works on in the Simulator :-)

Complete project in the ScrollViewScreenShot Github Repository

Dichy answered 22/8, 2010 at 3:4 Comment(22)
That seems to render each item on top of each other, so it doesn't look like the original display!Mensa
Oh but then you are close! Just set a transform to move the origin for each view before you render it.Dichy
I've spent hours trying to figure out how to do that without success. Can you give me any guidance?Mensa
You, sir, are my personal hero today.Mensa
Any idea how to scale down the generated image?Chaise
I think this cant be do in lazy loading scrollviewHighline
I agree with previous poster, personal hero for the day!!Christine
Great solution! but any ideas how to add subviews to image?Deboradeborah
Upvote for indentation trick, never thought of using braces like so.Forcefeed
Note: Make sure that your layoutWithSubViews is not interfering with your screenshot.Tyrr
WHERE EXACTLY is that screenshot saved when you use the Simulator on Xcode 5? The app's /tmp folder is empty!Neighborly
Great solution, but I used UIGraphicsBeginImageContextWithOptions(_scrollView.contentSize, NO, 0.0); for better resolution.Kristofer
I used this as an easy way to merge a bunch of images together onto a base Image. Problem I had was the UIImage that was returned was not the same size as the base image. Simple trick is also reset the zoomScale to 1.0 before you renderInContext... the UIScrollview contentSize will then match the size of your parent container in the UIScrollView.Freitag
using this method in iOS 13 devices, the offscreen part is black. it seems to work in the previous OS version, but not iOS 13?Annaleeannaliese
@AlenLiang The same problem with you. Did you solve it?Shotputter
@无夜之星辰 yes, i create the view without attach it to any superview, screenshot it. The code is like scrollView.frame = CGRect(x: 0, y: 0, width: width, height: height) and screenshot the view by creating a UIGraphicsImageRenderer(size: CGSize(width: width, height: height).Annaleeannaliese
@AlenLiang Good job.Shotputter
@AlenLiang I have tried above solution, but it is not working in iOS 13. Showing blank screen for down area. I am using TableView to show all the cells. Can you post your answer here to get full screenshot of UITableViewVeach
@Veach Try setting clipsToBound = falseMolliemollify
@Veach Were you able to resolve this? Could you please share the final solution?Molliemollify
@TusharKatyal No Tushar . If the content is big , I cannot able to take screenshot in iOS 14.Veach
in i0S 14 stil won't work for a really long text view, the image is the right size, but the text is cut off, any help with this?Catherinacatherine
C
26

For me, the currently accepted answer from Stefan Arentz didn't work.

I had to implement this on iOS 8 and above, and tested on the iPhone. The accepted answer just renders the visible part of a scroll view, while the rest of image remains blank.

I tried fixing this using drawViewHierarchyInRect - no luck. Depending on afterScreenUpdates being true or false I got stretched part of image or only part of the contents.

The only way I've found to achieve correct snapshotting of a UIScrollView's entire contents is to add it to another temporary view and then render it.

Sample code is below (scrollview is outlet in my VC)

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
}
Conceivable answered 5/2, 2015 at 11:49 Comment(4)
Great, the image is created but unfortunately my scrollview is not restored in the superview, any suggestions?Rustyrut
same issue as @Ettore mentionedBandage
Same issue. Unfortunately, my scroll view could not be restored. @Sam will you please help me out on this?Dobruja
Image had saved but my scrollView had been removed.Shotputter
R
10

Working Example of UIView Extension with handling for UIScrollView:

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!

        }
}
Recapture answered 2/12, 2016 at 2:46 Comment(3)
This works well-- I also posted a cleaner/safer solution on here as well based off of this.Pippin
Just use UIGraphicsBeginImageContextWithOptions(size, false, 0) for better quality... ;)Flavio
Not work when scrollView larger than screen on iOS 13.Shotputter
P
9

2023 UPDATE: Deepak Deore's answer seems to be the latest and greatest way. It captures content thats off screen with iOS 13 without needing to remove/re-add the scroll view.


I took this solution from @Roopesh Mittal's answer and made it safer/cleaner.

Swift 5 compatible

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
    }
}
Pippin answered 2/2, 2018 at 20:30 Comment(3)
in some cases, the frame and contentOffset won't be restored. and UIGraphicsEndImageContext won't be called.Chronometry
I haven't run into that case when using this code, but i'm sure its possible somehow. Either way, guarding and not crashing may be more desirable in some scenarios (like in my case)Pippin
Just add these two lines above context declaration to get higher quality image. let scale = UIScreen.main.scale UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);Zoography
C
8

A refined Swift 4.x/5.0 version, based on @RyanG 's answer:

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
    }
}
Chronometry answered 1/8, 2018 at 7:39 Comment(6)
in iOS 13 this didn't work for me :( the image was the size of the whole scrollview, but only the part visible on screen was renderedLeukocyte
@Leukocyte I had met the same problem, did you solve it?Shotputter
Also, having issues with iOS 13Hid
@Leukocyte did you find an answer for this problem? can't find itCatherinacatherine
@无夜之星辰 Sorry, I can't remember :( But I do see that I upvoted this answer https://mcmap.net/q/23431/-getting-a-screenshot-of-a-uiscrollview-including-offscreen-partsLeukocyte
@RogelioHeredia Sorry, I can't remember :( But I do see that I upvoted this answer https://mcmap.net/q/23431/-getting-a-screenshot-of-a-uiscrollview-including-offscreen-partsLeukocyte
T
8

In iOS 13 I have ran into issue that this line won't work

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

to fix the issue, I am removing scrollview from parent and then attaching in after taking the screenshot.

Full Code:

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
    }

Get Screenshot:

func getScreenshot() -> UIImage? {
     scrollView.removeFromSuperview()
     let image = scrollView.screenshot()
     addScrollView()
     return image
}
Triumphal answered 28/10, 2019 at 16:39 Comment(5)
removing scrollview from parent! that's it!Annaleeannaliese
Can you elaborate / share full code of how you do this? I am facing the issue where the screenshot is captured but the scrollView (or tableView in my case) disappears after.Toscanini
@Toscanini Updated for youTriumphal
@Triumphal Thank you for updating, however the solution doesn't include where "contentOffest" and "frame" are instantiated. And also it doesn't show any code for addScrollView()Toscanini
@Toscanini contentOffest and frame are properties in UIScrollView. addScrollView will depend on how you are adding scrollView to main viewTriumphal
B
4

SWIFT 3 version:

func snapshot() -> UIImage?
{      
    UIGraphicsBeginImageContext(scrollView.contentSize)

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

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

    scrollView.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()

    scrollView.contentOffset = savedContentOffset
    scrollView.frame = savedFrame

    UIGraphicsEndImageContext()

    return image
}

This worked for me

Bloodshot answered 7/2, 2018 at 6:9 Comment(2)
Worked perfectly in swift 4 too. +1 for the answer. Happy CodingAudre
It almost works. The problem is that the scrollview appears as a white space after the screen height. How can I solve this?Oehsen
I
4

As many have pointed, current solution doesn't work, and others solutions suggest removing scrollview from superview, which leads into loosing all the constraints.

Here I'm temporarily disabling all constraints related to scroll view and turn them on after screenshot is taken:

extension UIScrollView {
    func screenshot() -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
        // save the orginal offset, take a ref to all constraints related to the view
        let savedContentOffset = contentOffset
        let actualConstraints = relatedConstraints()
        // deactivate non needed constraints so they won't stop us from resiging scroll view
        NSLayoutConstraint.deactivate(actualConstraints)
        // enable auth generated constraints based on the frame
        translatesAutoresizingMaskIntoConstraints = true
        
        frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
        contentOffset = .zero
        defer {
            UIGraphicsEndImageContext()
            
            // reset original constraints
            translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate(actualConstraints)
            
            // layout superview needed before resetting content offset
            superview?.layoutIfNeeded()
            contentOffset = savedContentOffset
        }
        guard let ctx = UIGraphicsGetCurrentContext() else {
            return nil
        }
        layer.render(in: ctx)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        return image
    }
}

extension UIView {
    func relatedConstraints() -> [NSLayoutConstraint] {
        var constraints = self.constraints
        var parent = superview
        while parent != nil {
            constraints.append(contentsOf: parent!.constraints.filter { $0.firstItem === self || $0.secondItem === self })
            parent = parent!.superview
        }
        return constraints
    }
}
Iiette answered 27/7, 2021 at 10:0 Comment(0)
G
3

SWIFT 3 version thanks to @gleb vodovozov:

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

    UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, UIScreen.main.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 = CGPoint.zero;
    // set frame to content size
    self.scrollView.frame = CGRect(x: 0, y: 0, width: self.scrollView.contentSize.width, height: self.scrollView.contentSize.height)
    // remove background
    self.scrollView.backgroundColor = UIColor.clear

    // make temp view with scroll view content size
    // a workaround for issue when image on ipad was drawn incorrectly
    let tempView = UIView(frame: CGRect(x: 0, y: 0, width: self.scrollView.contentSize.width, height: 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.render(in: 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
}
Gouty answered 24/8, 2017 at 8:54 Comment(0)
E
2

Here's another way of doing it, which takes the zoom level into account. I have a scrollview with 4 different UIImageView layers in it, and I want to take a screenshot of their current state:

float theScale = 1.0f / theScrollView.zoomScale;
// The viewing rectangle in absolute coordinates
CGRect visibleArea = CGRectMake((int)(theScrollView.contentOffset.x * theScale), (int)(theScrollView.contentOffset.y * theScale),
                                (int)(theScrollView.bounds.size.width * theScale), (int)(theScrollView.bounds.size.height * theScale));

NSArray *layers = [NSArray arrayWithObjects:imageLayer1, imageLayer2, imageLayer3, imageLayer4, nil];
UIGraphicsBeginImageContext(visibleArea.size);
for (UIImageView *layer in layers) {
    CALayer *coreLayer = layer.layer;
    coreLayer.bounds = CGRectMake(layer.frame.origin.x - visibleArea.origin.x, layer.frame.origin.y - visibleArea.origin.y, layer.frame.size.width, layer.frame.size.height);
    [coreLayer renderInContext:UIGraphicsGetCurrentContext()];
}
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

This takes the screenshot in absolute coordinates. That is, if you have a 2048*2048 image in the scrollview and you can see about a quarter of it, then regardless of the resolution of your screen it would take a screenshot of 512*512. If you want to take a screenshot at your screen resolution (say, 320*480) then you have to adjust the image as follows, directly after the above code:

UIGraphicsBeginImageContext(theScrollView.frame.size);
[screenshot drawInRect:CGRectMake(0, 0, theScrollView.frame.size.width, theScrollView.frame.size.height)];
UIImage *smallScreenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Eichmann answered 2/6, 2011 at 1:40 Comment(0)
H
2

If you don't want to expand your scroll view beyond the entire screen (and it won't work with autolayout anyway) there's a better way.

You can use core graphics transforms in conjunction with the contentOffset of the scroll view to accomplish the same thing.

//
//  ScrollViewSnapshotter.swift
//  ScrollViewSnapshotter
//
//  Created by Moshe Berman on 4/10/16.
//  Copyright © 2016 Moshe Berman. All rights reserved.
//

import UIKit

class ScrollViewSnapshotter: NSObject {


func PDFWithScrollView(scrollview: UIScrollView) -> NSData {
    
    /**
     *  Step 1: The first thing we need is the default origin and size of our pages.
     *          Since bounds always start at (0, 0) and the scroll view's bounds give us
     *          the correct size for the visible area, we can just use that.
     *
     *          In the United States, a standard printed page is 8.5 inches by 11 inches,
     *          but when generating a PDF it's simpler to keep the page size matching the
     *          visible area of the scroll view. We can let our printer software (such
     *          as the Preview app on OS X or the Printer app on iOS) do the scaling.
     *
     *          If we wanted to scale ourselves, we could multiply each of those
     *          numbers by 72, to get the number of points for each dimension.
     *          We would have to change how we generated the the pages below, so
     *          for simplicity, we're going to stick to one page per screenful of content.
     */
    
    let pageDimensions = scrollview.bounds
    
    /**
     *  Step 2: Now we need to know how many pages we will need to fit our content.
     *          To get this, we divide our scroll views dimensions by the size
     *          of each page, in either direction.
     *          We also need to round up, so that the pages don't get clipped.
     */
    
    let pageSize = pageDimensions.size
    let totalSize = scrollview.contentSize
    
    let numberOfPagesThatFitHorizontally = Int(ceil(totalSize.width / pageSize.width))
    let numberOfPagesThatFitVertically = Int(ceil(totalSize.height / pageSize.height))
    
    /**
     *  Step 3: Set up a Core Graphics PDF context.
     *
     *          First we create a backing store for the PDF data, then
     *          pass it and the page dimensions to Core Graphics.
     *
     *          We could pass in some document information here, which mostly cover PDF metadata,
     *          including author name, creator name (our software) and a password to
     *          require when viewing the PDF file.
     *
     *          Also note that we can use UIGraphicsBeginPDFContextToFile() instead,
     *          which writes the PDF to a specified path. I haven't played with it, so
     *          I don't know if the data is written all at once, or as each page is closed.
     */
    
    let outputData = NSMutableData()
    
    UIGraphicsBeginPDFContextToData(outputData, pageDimensions, nil)
    
    /**
     *  Step 4: Remember some state for later.
     *          Then we need to clear the content insets, so that our
     *          core graphics layer and our content offset match up.
     *          We don't need to reset the content offset, because that
     *          happens implicitly, in the loop below.
     */
    
    let savedContentOffset = scrollview.contentOffset
    let savedContentInset = scrollview.contentInset
    
    scrollview.contentInset = UIEdgeInsetsZero
    
    /**
     *  Step 6: Now we loop through the pages and generate the data for each page.
     */
    
    if let context = UIGraphicsGetCurrentContext()
    {
        for indexHorizontal in 0 ..< numberOfPagesThatFitHorizontally
        {
            for indexVertical in 0 ..< numberOfPagesThatFitVertically
            {
                
                /**
                 *  Step 6a: Start a new page.
                 *
                 *          This automatically closes the previous page.
                 *          There's a similar method UIGraphicsBeginPDFPageWithInfo,
                 *          which allows you to configure the rectangle of the page and
                 *          other metadata.
                 */
                
                UIGraphicsBeginPDFPage()
                
                /**
                 *  Step 6b:The trick here is to move the visible portion of the
                 *          scroll view *and* adjust the core graphics context
                 *          appropriately.
                 *
                 *          Consider that the viewport of the core graphics context
                 *          is attached to the top of the scroll view's content view
                 *          and we need to push it in the opposite direction as we scroll.
                 *          Further, anything not inside of the visible area of the scroll
                 *          view is clipped, so scrolling will move the core graphics viewport
                 *          out of the rendered area, producing empty pages.
                 *
                 *          To counter this, we scroll the next screenful into view, and adjust
                 *          the core graphics context. Note that core graphics uses a coordinate
                 *          system which has the y coordinate decreasing as we go from top to bottom.
                 *          This is the opposite of UIKit (although it matches AppKit on OS X.)
                 */
                
                let offsetHorizontal = CGFloat(indexHorizontal) * pageSize.width
                let offsetVertical = CGFloat(indexVertical) * pageSize.height
                
                scrollview.contentOffset = CGPointMake(offsetHorizontal, offsetVertical)
                CGContextTranslateCTM(context, -offsetHorizontal, -offsetVertical) // NOTE: Negative offsets
                
                /**
                 *  Step 6c: Now we are ready to render the page.
                 *
                 *  There are faster ways to snapshot a view, but this
                 *  is the most straightforward way to render a layer
                 *  into a context.
                 */
                
                scrollview.layer.renderInContext(context)
            }
        }
    }
    
    /**
     *  Step 7: End the document context.
     */
    
    UIGraphicsEndPDFContext()
    
    /**
     *  Step 8: Restore the scroll view.
     */
    
    scrollview.contentInset = savedContentInset
    scrollview.contentOffset = savedContentOffset
    
    /**
     *  Step 9: Return the data.
     *          You can write it to a file, or display it the user,
     *          or even pass it to iOS for sharing.
     */
    
    return outputData
}
}

Here's a blog post I wrote explaining the process.

The process for generating a PDF is very similar to snapshotting an image, except instead of pages, you'd need to make one large canvas that matches the size of the scroll view and then grab the contents in chunks.

Hammer answered 11/4, 2016 at 13:39 Comment(0)
T
2

Swift 5 Version

    extension UIScrollView {
    
    func takeScrollViewScreenShot() -> UIImage? {
        
        UIGraphicsBeginImageContext(self.contentSize)
        
        let savedContentOffset = self.contentOffset
        let savedFrame = self.frame
        
        self.contentOffset = CGPoint.zero
        self.layer.frame = CGRect(x: 0, y: 0, width: self.contentSize.width, height: self.contentSize.height)
        
        self.layer.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        
        self.contentOffset = savedContentOffset
        self.frame = savedFrame
        UIGraphicsEndImageContext()
        return image
    }
}
Tuck answered 5/8, 2022 at 8:5 Comment(1)
This should be the new answer in 2023! I did't have to remove the scrollview or any of that. The one addition here I would add to improve quality is change the first line in the func to UIGraphicsBeginImageContextWithOptions(this.contentSize, false, 0)Pippin
D
1

I have found below code and its working for me. try this ..

extension UIView {
func capture() -> UIImage {
    UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
    drawHierarchy(in: self.bounds, afterScreenUpdates: true)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}}
Dobby answered 8/11, 2020 at 18:35 Comment(0)
C
1

Normally I wouldn't recommend using a library but... Use the SnapshotKit. It works like a charm and The code looks alright too. Using it is straight forward:

Objective-C:

UIImage *tableViewScreenShot = [yourTableView takeSnapshotOfFullContent];

Swift:

let tableViewScreenShot: UIImage = yourTableView.takeSnapshotOfFullContent()
Chapter answered 3/2, 2021 at 16:24 Comment(1)
Yes, the SnapshotKit works like a charm! I tried "async_takeSnapshotOfFullContent()" for scrollview snapshot. private func async_takeSnapshotOfFullContent() { self.aScrollView.asyncTakeSnapshotOfFullContent { (image) in UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil) } } @IBAction func buttonTap(_ sender: Any) { async_takeSnapshotOfFullContent() }Angelicangelica
L
0

It seems to me that the accepted solution can be fixed by updating scrollView.layer.frame rather than scrollView.frame, as pointed out here. I am not sure I actually understand why this works, though!

Landes answered 8/5, 2022 at 7:51 Comment(0)
P
-2

I don't know much but I can guess that if we set the size of the contextRect like this for landscape, it may work well:

  CGRect contextRect  = CGRectMake(0, 0, 1004, 768*2);

Because this contextRect will determine the size of the UIGraphicsBeginImageContext so I hope that double the height can solve your problem

Primogeniture answered 22/8, 2010 at 1:54 Comment(1)
It seems that it merely cuts off at the end of the visible area. Increasing the height doesn't help.Mensa

© 2022 - 2024 — McMap. All rights reserved.