在 UIScrollView 中拍摄 CATiledLayer 支持的视图的图像快照

发布于 2025-01-02 00:00:28 字数 896 浏览 2 评论 0原文

我有一个由 UIScrollView 组成的自定义地图视图。滚动视图的子视图由 CATiledLayer 支持。这里一切都很好。平移和平移缩放加载新的地图图块,一切都表现良好。

我想要做的是将动画视频帧捕获到此滚动视图。本质上,我想创建一个滚动视图的 contentOffsetzoomScale 的动画更改视频。

我知道这个概念是合理的,因为我可以获得私有 API 函数 UIGetScreenImage() 来以 10fps 的速度捕获应用程序的屏幕,组合这些图像,并且我可以获得流畅且具有滚动视图动画使用的时序曲线。

当然,我的问题是我无法使用私有 API。浏览一下苹果这里概述的替代方案,给我留下了几乎一个据说有效的选择:询问 < code>CALayer 到 renderInContext 并从中获取 UIGraphicsGetImageFromCurrentImageContext()

不过,这似乎不适用于 CATiledLayer 支持的视图。捕获的是块状、未缩放的图像,就好像从未加载更高分辨率的图块一样。考虑到 CATiledLayer 在后台线程中绘制以提高性能,并且从主线程调用 renderInContext 可能无法捕获这些更新,这在某种程度上是有道理的。即使我也渲染平铺层的 presentationLayer,结果也是相似的。

是否有 Apple 认可的方法可以在包含滚动视图的动画过程中捕获 CATiledLayer 支持的视图的图像?或者在任何时候,就此而言?

I've got a custom map view which is made of a UIScrollView. The scroll view's subview is backed by a CATiledLayer. Everything works great here. Panning & zooming loads up new map tiles and everything performs well.

What I want to do is capture frames of video of animations to this scroll view. Essentially, I want to create a video of animated changes to the scroll view's contentOffset and zoomScale.

I know that the concept is sound as I can get the private API function UIGetScreenImage() to capture the app's screen at, say, 10fps, combine these images, and I get playback animations that are smooth and have the timing curves used by the scroll view animations.

My problem, of course, is that I can't use the private API. Going through the alternatives outlined by Apple here leaves me with pretty much one supposedly valid option: asking a CALayer to renderInContext and taking a UIGraphicsGetImageFromCurrentImageContext() from that.

This just doesn't seem to work with CATiledLayer-backed views, though. A blocky, un-zoomed image is what is captured, as if the higher-resolution tiles never load. This somewhat makes sense given that CATiledLayer draws in background threads for performance and calling renderInContext from the main thread might not catch these updates. The result is similar even if I render the tiled layer's presentationLayer as well.

Is there an Apple-sanctioned way of capturing an image of a CATiledLayer-backed view during the course of the containing scroll view's animations? Or at any point, for that matter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

秉烛思 2025-01-09 00:00:28

顺便说一句,如果您在 CATiledLayer 支持的视图中正确实现 renderLayer:inContext:,这是可行的。

BTW, this is doable if you properly implement renderLayer:inContext: in your CATiledLayer-backed view.

眼角的笑意。 2025-01-09 00:00:28

我做了一个快速测试,并在包装​​滚动视图的视图上使用 renderInContext: 似乎有效。你尝试过吗?

I did a quick test, and using renderInContext: on a view wrapping the scroll view seemed to work. Have you tried that?

还在原地等你 2025-01-09 00:00:28

这段代码对我有用。

- (UIImage *)snapshotImageWithView:(CCTiledImageScrollView *)view
{
// Try our best to approximate the best tile set zoom scale to use
CGFloat tileScale;
if (view.zoomScale >= 0.5) {
    tileScale = 2.0;
}
else if (view.zoomScale >= 0.25) {
    tileScale = 1.0;
}
else {
    tileScale = 0.5;
}

// Calculate the context translation based on how far zoomed in or out.
CGFloat translationX = -view.contentOffset.x;
CGFloat translationY = -view.contentOffset.y;
if (view.contentSize.width < CGRectGetWidth(view.bounds)) {
    CGFloat deltaX = (CGRectGetWidth(view.bounds) - view.contentSize.width) / 2.0;
    translationX += deltaX;
}
if (view.contentSize.height < CGRectGetHeight(view.bounds)) {
    CGFloat deltaY = (CGRectGetHeight(view.bounds) - view.contentSize.height) / 2.0;
    translationY += deltaY;
}

// Pass the tileScale to the context because that will be the scale used in drawRect by your CATiledLayer backed UIView
UIGraphicsBeginImageContextWithOptions(CGSizeMake(CGRectGetWidth(view.bounds) / view.zoomScale, CGRectGetHeight(view.bounds) / view.zoomScale), NO, tileScale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, translationX / view.zoomScale, translationY / view.zoomScale);

// The zoomView is a subview of UIScrollView. The CATiledLayer backed UIView is a subview of the zoomView.
[view.zoomView.layer renderInContext:context];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return image;

完整的示例代码在这里

找到:https://github.com/gortega56/CCCanvasView

This code works for me.

- (UIImage *)snapshotImageWithView:(CCTiledImageScrollView *)view
{
// Try our best to approximate the best tile set zoom scale to use
CGFloat tileScale;
if (view.zoomScale >= 0.5) {
    tileScale = 2.0;
}
else if (view.zoomScale >= 0.25) {
    tileScale = 1.0;
}
else {
    tileScale = 0.5;
}

// Calculate the context translation based on how far zoomed in or out.
CGFloat translationX = -view.contentOffset.x;
CGFloat translationY = -view.contentOffset.y;
if (view.contentSize.width < CGRectGetWidth(view.bounds)) {
    CGFloat deltaX = (CGRectGetWidth(view.bounds) - view.contentSize.width) / 2.0;
    translationX += deltaX;
}
if (view.contentSize.height < CGRectGetHeight(view.bounds)) {
    CGFloat deltaY = (CGRectGetHeight(view.bounds) - view.contentSize.height) / 2.0;
    translationY += deltaY;
}

// Pass the tileScale to the context because that will be the scale used in drawRect by your CATiledLayer backed UIView
UIGraphicsBeginImageContextWithOptions(CGSizeMake(CGRectGetWidth(view.bounds) / view.zoomScale, CGRectGetHeight(view.bounds) / view.zoomScale), NO, tileScale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, translationX / view.zoomScale, translationY / view.zoomScale);

// The zoomView is a subview of UIScrollView. The CATiledLayer backed UIView is a subview of the zoomView.
[view.zoomView.layer renderInContext:context];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return image;

}

Full sample code found here: https://github.com/gortega56/CCCanvasView

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文