如何更快地将视图渲染到图像中?

NOr*_*der 21 core-graphics calayer uiview ios ios6

我正在制作放大镜应用程序,允许用户触摸屏幕并移动他的手指,将有一个放大镜与他的手指路径.我通过截屏实现它并将图像分配给放大镜图像视图,如下所示:

    CGSize imageSize = frame.size;
    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextScaleCTM(c, scaleFactor, scaleFactor);
    CGContextConcatCTM(c, CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y));
    [self.layer renderInContext:c];
    UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return screenshot;
Run Code Online (Sandbox Code Playgroud)

问题是这个问题很self.layer renderInContext慢,因此用户在移动手指时感觉不顺畅.self.layer renderInContext然而,我试图在其他线程中运行,它使放大镜图像看起来很奇怪,因为放大镜中的图像显示延迟.

有没有更好的方法将视图渲染成图像?确实renderInContext:使用GPU?

Jan*_*ano 82

在iOS6中,renderInContext:是唯一的方法.这很慢.它使用CPU.

如何呈现UIKit内容

renderInContext:

[view.layer renderInContext:UIGraphicsGetCurrentContext()];
Run Code Online (Sandbox Code Playgroud)
  • 需要iOS 2.0.它在CPU中运行.
  • 它不捕获具有非仿射变换,OpenGL或视频内容的视图.
  • 如果动画正在运行,您可以选择捕获:
    • view.layer,它捕获动画的最后一帧.
    • view.presentationLayer,捕获动画的当前帧.

snapshotViewAfterScreenUpdates:

UIView *snapshot = [view snapshotViewAfterScreenUpdates:YES];
Run Code Online (Sandbox Code Playgroud)
  • 需要iOS 7.
  • 这是最快的方法.
  • 视图contents是不可变的.如果你想申请效果不好.
  • 它捕获所有内容类型(UIKit,OpenGL或视频).

resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets

[view resizableSnapshotViewFromRect:rect afterScreenUpdates:YES withCapInsets:edgeInsets]
Run Code Online (Sandbox Code Playgroud)
  • 需要iOS 7.
  • snapshotViewAfterScreenUpdates:与可调整大小的插件相同但是相同.content也是一成不变的.

drawViewHierarchyInRect:afterScreenUpdates:

[view drawViewHierarchyInRect:rect afterScreenUpdates:YES];
Run Code Online (Sandbox Code Playgroud)
  • 需要iOS 7.
  • 它吸收了当前的背景.
  • 根据会议226,它比...更快renderInContext:.

请参阅WWDC 2013 Session 226在iOS上实现关于新快照API的Engaging UI.


如果有任何帮助,这里有一些代码可以在一个仍在运行时丢弃捕获尝试.

这会限制一次阻止执行一个,并丢弃其他人.从这个SO答案.

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t renderQueue = dispatch_queue_create("com.throttling.queue", NULL);

- (void) capture {
    if (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW) == 0) {
        dispatch_async(renderQueue, ^{
            // capture
            dispatch_semaphore_signal(semaphore);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

这是做什么的?

  • 为一(1)个资源创建信号量.
  • 创建一个串行队列.
  • DISPATCH_TIME_NOW表示超时为无,因此在红灯时立即返回非零值.因此,不执行if内容.
  • 如果是绿灯,请异步运行该块,然后再次设置绿灯.