ios - 如何更快地将 View 渲染成图像?

标签 ios ios6 uiview core-graphics calayer

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

    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;

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

有没有更好的方法将 View 渲染成图像? renderInContext: 使用 GPU 吗?

最佳答案

没有。在 iOS6 中,renderInContext: 是唯一的方法。它很慢。它使用 CPU。

呈现 UIKit 内容的方法

渲染上下文:

[view.layer renderInContext:UIGraphicsGetCurrentContext()];
  • 需要 iOS 2.0。 It runs in the CPU .
  • 它不使用非仿射变换、OpenGL 或视频内容捕获 View 。
  • 如果动画正在运行,您可以选择捕捉:
    • view.layer,它捕获动画的最后一帧。
    • view.presentationLayer,捕获动画的当前帧。

snapshotViewAfterScreenUpdates:

UIView *snapshot = [view snapshotViewAfterScreenUpdates:YES];
  • 需要 iOS 7。
  • 这是最快的方法。
  • View 内容 是不可变的。如果您想应用效果,那就不好了。
  • 它捕获所有内容类型(UIKit、OpenGL 或视频)。

resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets

[view resizableSnapshotViewFromRect:rect afterScreenUpdates:YES withCapInsets:edgeInsets]
  • 需要 iOS 7。
  • snapshotViewAfterScreenUpdates: 相同,但具有可调整大小的插图。 content 也是不可变的。

drawViewHierarchyInRect:afterScreenUpdates:

[view drawViewHierarchyInRect:rect afterScreenUpdates:YES];
  • 需要 iOS 7。
  • 它在当前上下文中绘制。
  • 根据 session 226,它比 renderInContext: 更快。

参见 WWDC 2013 session 226 Implementing Engaging UI on iOS关于新的快照 API。


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

这会限制一次一个 block 的执行,并丢弃其他 block 。来自 this SO answer .

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

这是在做什么?

  • 为一 (1) 种资源创建信号量。
  • 创建一个串行队列。
  • DISPATCH_TIME_NOW 表示没有超时,所以它在红灯时立即返回非零值。因此,不执行 if 内容。
  • 如果绿灯,异步运行 block ,并再次设置绿灯。

关于ios - 如何更快地将 View 渲染成图像?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19066717/

相关文章:

ios - 在 UIImageView IOS 上画圆的最佳方法

ios - Swift 3 将两种颜色放在同一个 UIView 中

iphone - ABPerson 中发短信的默认电话号码

ios - 从 Swift 中的其他类调用方法

ios - 如何传递与表中选定行对应的核心数据?

来自其他用户的 iOS SoundCloud 流

ios - 导航栏大标题过渡代表

ios - Swift 并发操作慢 2 倍

ajax - 防止 iOS 6 缓存 Ajax POST 请求

ios - PaintCode - 更新 View 并传递变量