在iOS中如何将整个UITableView渲染为UIImage?

9

我有一个简单的UITableView,其中包含一些数据。表格的高度大于屏幕大小。现在我需要捕获这个表格的截图(整个表格)。我知道内容后面的单元格会被回收,但也许有一种方法可以捕获它们。不是吗?


请问您能否帮我检查一下附上的问题并提供帮助。我正在尝试将TableView更改为ImageView。 https://stackoverflow.com/questions/70879552/uitableview-to-uiimage-swift。 - Sham Dhiman
14个回答

8

试试这个:

 func generateImage(tblview:UITableView) ->UIImage{
        UIGraphicsBeginImageContext(tblview.contentSize);
        tblview.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
        tblview.layer.renderInContext(UIGraphicsGetCurrentContext())
        let row = tblview.numberOfRowsInSection(0)
        let numberofRowsThatShowOnScreen = 4
        var scrollCount = row / numberofRowthatShowinscreen
        
        for var i = 0; i < scrollCount; i++ {
            tblview.scrollToRowAtIndexPath(NSIndexPath(forRow: (i+1) * numberofRowsThatShowOnScreen, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
            tblview.layer.renderInContext(UIGraphicsGetCurrentContext())
        }
        
        let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext();
        return image;
}


        
 

并在调用此方法后执行。
 var imageView = UIImageView()
     imageView.image = generateImage(tableView)

2
这仅仅只是为我捕捉屏幕上的单元格。 - Workshed
你好@Parth,能否请您帮忙查看并回答我附上的问题。我正在尝试将TableView更改为ImageView。 https://stackoverflow.com/questions/70879552/uitableview-to-uiimage-swift。 - Sham Dhiman

3

我的Swift 4解决方案!

import Foundation
import UIKit

extension UITableView {

    func asFullImage() -> UIImage? {

        guard self.numberOfSections > 0, self.numberOfRows(inSection: 0) > 0 else {
            return nil
        }

        self.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false)

        var height: CGFloat = 0.0
        for section in 0..<self.numberOfSections {
            var cellHeight: CGFloat = 0.0
            for row in 0..<self.numberOfRows(inSection: section) {
                let indexPath = IndexPath(row: row, section: section)
                guard let cell = self.cellForRow(at: indexPath) else { continue }
                cellHeight = cell.frame.size.height
            }
            height += cellHeight * CGFloat(self.numberOfRows(inSection: section))
        }

        UIGraphicsBeginImageContextWithOptions(CGSize(width: self.contentSize.width, height: height), false, UIScreen.main.scale)

        for section in 0..<self.numberOfSections {
            for row in 0..<self.numberOfRows(inSection: section) {
                let indexPath = IndexPath(row: row, section: section)
                guard let cell = self.cellForRow(at: indexPath) else { continue }
                cell.contentView.drawHierarchy(in: cell.frame, afterScreenUpdates: true)

                if row < self.numberOfRows(inSection: section) - 1 {
                    self.scrollToRow(at: IndexPath(row: row+1, section: section), at: .bottom, animated: false)
                }
            }
        }
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image!
    }
}


可以运行。但是图片显示不正确。 - cvdogan

2

将UITableView的cell转换为UIImage

UIGraphicsBeginImageContextWithOptions(CGSizeMake(tableView.contentSize.width, tableView.contentSize.height+64+40), true, 0)

for section in 0...tableView.numberOfSections-1 {
    for row in 0...tableView.numberOfRowsInSection(section)-1 {
        let indexPath = NSIndexPath.init(forRow: row, inSection: section)
        let cell = tableView.cellForRowAtIndexPath(indexPath)!

        print("row:\(indexPath.row), frame:\(cell.frame) height:\(cell.frame.height)")

        cell.contentView.drawViewHierarchyInRect(cell.frame, afterScreenUpdates: true)
    }
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

为什么被点赞?当你尝试访问不可见的(并且没有被绘制)的单元格时,它崩溃了。 - Gargo

1

Young Hoo Kim的回答的Swift 3版本适用于我:

func generateTableViewImage(_ tableView: UITableView) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(tableView.contentSize, false, UIScreen.main.scale)

    for section in 0..<tableView.numberOfSections {
        for row in 0..<tableView.numberOfRows(inSection: section) {
            let indexPath = IndexPath(row: row, section: section)
            guard let cell = tableView.cellForRow(at: indexPath) else { continue }
            cell.contentView.drawHierarchy(in: cell.frame, afterScreenUpdates: true)
        }
    }
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}

请注意,如果你的UITableView特别长,可能无法使用上述函数捕获它,因为它可能需要太大的图像。

1

Parth Bhadaja的回答的Swift 4版本:

func generateImage(tableView:UITableView) -> UIImage {
    UIGraphicsBeginImageContext(tableView.contentSize);
    tableView.scrollToRow(at: NSIndexPath(row: 0, section: 0) as IndexPath, at: UITableViewScrollPosition.top, animated: false)
    tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
    let row = tableView.numberOfRows(inSection: 0)
    let numberofRowthatShowinscreen = 4
    let scrollCount = row / numberofRowthatShowinscreen

    for i in 0..<scrollCount {
        tableView.scrollToRow(at: NSIndexPath(row: (i+1)*numberofRowthatShowinscreen, section: 0) as IndexPath, at: UITableViewScrollPosition.top, animated: false)
        tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
    }

    let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext();
    return image;
}

应该注意的是,这个函数确实会返回整个tableView的图像,即使是尚未加载到屏幕上的单元格。

在iOS 14中使用Swift 5无法工作。屏幕外部分仅为白色。 - Mikrasya

1

要截取一个 UIView 的屏幕截图,可以使用以下代码:

UIGraphicsBeginImageContext(tableview.frame.size)
tableview.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext() 

这将适用于可见的任何内容,并且可能适用于任何已加载但未显示的视图。
在UITableView中,仅加载可见单元格以及表格两端的1个离屏单元格。其他单元格实际上不存在...但是你无法“捕获”它们。

0
通过使用category,向UITableView添加render方法。
- (UIImage *)render {

    //save the origin info for later to restore
    CGPoint originContentOffset = self.contentOffset;

    //force jump to table view's top
    self.contentOffset = CGPointZero;

    UIGraphicsBeginImageContext(self.contentSize);

    CGContextRef *ctx = UIGraphicsGetCurrentContext();

    //render header 
    [self.tableHeaderView.layer renderInContext:ctx];

    //render sections
    NSInteger numberOfSections = [self numberOfSections];
    for (int section = 0; section < numberOfSections; section++) {
        NSInteger numberOfRows = [self numberOfRowsInSection:section];
        for (int row = 0; row < numberOfRows; row++) {
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
            //check cell is visible
            if ([self cellForRowAtIndexPath:indexPath]) {
                [self scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:false];
                [self.layer renderInContext:ctx];
            }
        }
    }

    //render footer
    [self.tableFooterView.layer renderInContext:ctx];
    
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    //restore info
    self.contentOffset = originContentOffset;

    return image;
}

0

将Parth Bhadaja的答案转换为Swift 3,并作为扩展使用计算属性:

extension UITableView {
    var capturedImage : UIImage {
        UIGraphicsBeginImageContext(contentSize);
        scrollToRow(at: NSIndexPath(row: 0, section: 0) as IndexPath, at:     UITableViewScrollPosition.top, animated: false)
        layer.render(in: UIGraphicsGetCurrentContext()!)
        let row = numberOfRows(inSection: 0)
        let numberofRowthatShowinscreen = 4
        let scrollCount = row / numberofRowthatShowinscreen

        for i in 0 ..< scrollCount  {
            scrollToRow(at: NSIndexPath(row: (i+1)*numberofRowthatShowinscreen, section: 0) as     IndexPath, at: UITableViewScrollPosition.top, animated: false)
            layer.render(in: UIGraphicsGetCurrentContext()!)
        }

        let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext();
        return image;
    }
}

使用方法:

let image = tableView.capturedImage

0

您可以使用tableView.contentSize的大小创建图形上下文。之后,您必须遍历所有indexPath并绘制每个单元格。您可以有一个单元格用于重复使用以绘制屏幕外的单元格 - 只需更改所需数据的内容并进行绘制即可。


0

这里提供一种替代方案,带有一些小的改进:

  • 它基于内容偏移而不是行数来滚动内容。
  • 如果可用,它会考虑头部和尾部视图(tableHeaderViewtableFooterView)。如果从第一行开始捕获,有时无法完全获取内容。
  • 在处理开始之前,存储contentOffset以便在滚动+捕获完成后再次设置它。
extension UITableView {
    
    var tableViewAsImage: UIImage? {
        let offset = contentOffset
        UIGraphicsBeginImageContext(contentSize)

        guard let currentContext = UIGraphicsGetCurrentContext() else {
            return nil
        }
        setContentOffset(.zero, animated: false)
        layer.render(in: currentContext)
        
        let height = bounds.height
        let totalHeight = contentSize.height
        let scrollCount = Int((height / totalHeight).rounded())
        
        for i in 0...scrollCount {
            let y: CGFloat = CGFloat(i) * height
            setContentOffset(.init(x: 0, y: y), animated: false)
            layer.render(in: currentContext)
        }
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        setContentOffset(offset, animated: false)
        return image
    }
    
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接