UITableView的屏幕截图

12

我知道有很多关于这个主题的问题,但我确实已经阅读了所有的问题,但没有找到答案。

我想从当前的表格视图中截取屏幕截图。我是这样做的:

-(UIImage *)imageFromCurrentView
{
    UIGraphicsBeginImageContextWithOptions(self.tableView.bounds.size, YES, 1);
    [self.tableView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
}

一切都很顺利,但当我在表格视图中向下滚动并截屏时,图片的一半是黑色的。我不知道如何从实际的表格视图区域截屏。

7个回答

24

这将获取TableView的当前可见区域。

如果您想呈现整个TableView,首先需要知道除了一些少于几十行的情况之外,由于内存等原因这可能不是个好主意。最大图像尺寸为2048x2048,因此您的tableView的高度不能超过这个值。

UIView *viewToRender = self.tableView;
CGPoint contentOffset = self.tableView.contentOffset;

UIGraphicsBeginImageContext(viewToRender.bounds.size);

CGContextRef ctx = UIGraphicsGetCurrentContext();

//  KEY: need to translate the context down to the current visible portion of the tablview
CGContextTranslateCTM(ctx, 0, -contentOffset.y);

[viewToRender.layer renderInContext:ctx];

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

1
这应该被标记为正确答案,至少对我来说是这样修复的。CGContextTranslateCTM(UIGraphicsGetCurrentContext(),0,-contentOffset.y); - Marc
这回答了我的问题,即如何截取已滚动的UIScrollView(UITableView或其他)。当截取已滚动的scrollview时,我得到了大小与滚动偏移量相同的黑色条纹。所以非常感谢你,谢谢你,谢谢你。 - Rob Booth

22

请看这个我制作的,它可以完美解决你的需求,拥有以下十分实用的方法:

  1. 对UITableView进行全屏截图(将所有单元格渲染在一个UIImage上)
  2. 根据indexPath截取指定单元格的截图
  3. 对UITableView的表头、表尾或行进行截图

还有更多功能...

你可以通过Cocoapods添加到项目中,也可以手动将源文件拖入工程。

它非常易用:

UIImage * tableViewScreenshot = [self.tableView screenshot];

我希望您像我一样发现这个库非常有用。


1
到目前为止,这是我见过最令人兴奋的库 :) 非常感谢! - Vahan

3
以下代码在我的应用程序中完美地运行,您可以使用此代码捕获tableview或具有水平滚动的任何其他滚动视图的屏幕截图。
CGPoint offset = [_tableView contentOffset];
_tableView.contentOffset = CGPointMake(0.0, 0.0);
CGRect visibleRect = _tableView.bounds;
UIGraphicsBeginImageContextWithOptions(_tableView.contentSize, _tableView.opaque, 0.0);
[_tableView.layer renderInContext:UIGraphicsGetCurrentContext()];
while (_tipsTableView.contentSize.height>= (visibleRect.origin.y+visibleRect.size.height)) {
visibleRect.origin.y += visibleRect.size.height;
[_tableView scrollRectToVisible:visibleRect animated:NO]; [_tableView.layer renderInContext:UIGraphicsGetCurrentContext()]; }
UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
_tableView.contentOffset = offset;

2
由于UITableViewCell是可重用的,当滚动UITableView时,视口之外的单元格将被加入重用队列并从UITableView中移除。因此,请尽可能只在其索引路径上重用单元格:
- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  NSString *cellIdentifier = [NSString stringWithFormat:@"Cell_%d_%d", indexPath.section, indexPath.row]; 
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
  if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithReuseIdentifier:cellIdentifier] autorelease];
  }
  ...
  return cell;
}

尚未测试,可以试试看。是的,这将消耗更多的内存,仅用于截屏目的。

编辑:如果您关心内存使用情况,可以先对当前视口进行截屏,然后以编程方式向上和向下滚动以获取其他部分的屏幕截图,并最后组合所有的屏幕截图。


谢谢你的回答。我不太确定你所说的“仅用于截图目的”是什么意思。现在我有一个按钮可以截屏。当我按下截屏按钮时,如何处理这段代码而不会占用更多内存? - user656219
通常情况下,视口可以容纳的单元格数量仅略多于或等于视图端口,当滚动表视图时它们是可重复使用的,因此即使包含大量项目,它也可以平稳运行。而我这里的方法,单元格的数量与表视图的所有行数相同,因此如果您的表视图有大量项目,它将比正常方式占用更多的内存。所以请注意。如果您首先考虑内存使用,请不要使用此方法,尽管它可以工作。 - cxa
我遇到了完全相同的问题,这个方法对我没有解决。@Missaq,这个方法对你有用吗? - Marc
1
嗨,我知道这有点过时,但对于其他正在寻找的人,您可以查看此库:https://github.com/davidman/DHSmartScreenshot - David H.

1
- (UIImage *) imageWithView:(UIView *)cellTobeCaptured isImage:(BOOL)isImage
{

//If UITableViewCell contains UIImageView

    if(isImage){
     UIGraphicsBeginImageContextWithOptions(cellTobeCaptured.bounds.size, NO, [UIScreen mainScreen].scale);
     [cellTobeCaptured drawViewHierarchyInRect:CGRectMake(0, 0, cellTobeCaptured.bounds.size.width, cellTobeCaptured.bounds.size.height) afterScreenUpdates:NO];
     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();
     return image;
    }
else{
  UIGraphicsBeginImageContextWithOptions(cellTobeCaptured.bounds.size, cellTobeCaptured.opaque, 0.0);
  [cellTobeCaptured.layer renderInContext:UIGraphicsGetCurrentContext()];
   UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext();
   return img;
   }
}

将该方法作为调用:
 UITableViewCell *cell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:arrIndex inSection:0]];
 self->screenShotImage = [self imageWithView:cell isImage:YES];

0

对于Swift 5,您可以使用以下代码

    let viewToRender = tableView;
    let contentOffset = tableView.contentOffset

    UIGraphicsBeginImageContext(viewToRender.bounds.size)

    if let ctx = UIGraphicsGetCurrentContext() {
       ctx.translateBy(x: 0, y: -contentOffset.y)

       viewToRender.layer.render(in: ctx)

       let image = UIGraphicsGetImageFromCurrentImageContext();

    }
    UIGraphicsEndImageContext();

0

我曾经遇到过类似的问题,尝试在使用UITableView构建自定义日历时复制苹果日历滚动以更改月份。在调用scrollToRowAtIndexPath:之后,确保animated:为YES,以便调用scrollViewDidEndScrollingAnimation:,在scrollViewDidEndScrollingAnimation:中,在renderInContext:之前调用tableView上的reloadData:。然后整个tableView被渲染成图像。最终我没有长期使用它,所以不确定它作为永久解决方案的可靠性,但在我尝试时它运行良好。


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