“[CALayer renderInContext]”在 iPhone X 上崩溃

"[CALayer renderInContext]" crashes on iPhone X

我有一个自定义 UIView,我想将其呈现为 UIImage。自定义 UIViewUIImageView 的子类。

在此视图中,我正在渲染一些 UI 元素(在图像上绘制一堆圆圈)。添加的圈子数量可以达到数千个。

我正在使用这个简单的代码片段将视图呈现为 UIImage:

// Create the UIImage (code runs on the main thread inside an @autorelease pool)
UIGraphicsBeginImageContextWithOptions(viewToSave.image.size, viewToSave.opaque, 1.0);
[viewToSave.layer renderInContext:UIGraphicsGetCurrentContext()];
imageToSave = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

// Since I'm rendering some stuff inside the .layer of the UIView,
// I don't think I can use "drawViewHierarchyInRect: afterScreenUpdates:"

这是从 Instruments 中获取的内存分配,在一个添加了约 3000 个圆圈作为子视图的示例中:

现在这是奇怪的部分...这运行良好,我可以多次渲染图像(连续)并将其保存在 iPhone 5、iPhone 5s 等设备上的图库中, iPhone 6s, iPad Air 2, iPad Mini 4... 但是同样的代码在iPhone X[=35=上触发内存警告] 并最终使应用程序崩溃...

不幸的是,我无法访问 iPhone X,而报告此事的人也无法访问 Mac,因此我无法进行更深入的调查。

我真的不知道我做错了什么......你知道 iPhone X 是否有什么不同吗?我已经为这个问题苦苦挣扎了一段时间...

我想这个问题与 CALayer:renderInContext: 如何在需要按比例放大的上下文中处理数千个视图的绘制有关。是否可以尝试自己渲染子视图?然后用instrumentation对比验证是否效果更好

UIImage *imageToSave = [self imageFromSubLayer:viewToSave];

- (UIImage *)imageFromSubLayers:(UIImageView *)imageView {
    CGSize size = imageView.image.size;
    UIGraphicsBeginImageContextWithOptions(size, YES, .0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    for (CALayer *layer in imageView.layer.sublayers)
        [layer renderInContext:context];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}

我最近在我正在制作的应用程序中编写了一个方法,用于将 UIView 转换为 UIImage(这样我就可以在进度 views/tab 条上显示渐变)。我最终选择了以下代码,我正在使用此代码来呈现标签栏按钮,它适用于包括 X 在内的所有设备。

Objective C:

UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:gradientView.bounds.size];
    UIImage *gradientImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
        [gradientView drawViewHierarchyInRect:gradientView.bounds afterScreenUpdates:true];
    }];

Swift 4:

let renderer = UIGraphicsImageRenderer(size: gradientView.bounds.size)
        let image = renderer.image { ctx in
            gradientView.drawHierarchy(in: gradientView.bounds, afterScreenUpdates: true)
        }

我在我编写的示例项目中使用了这段代码,这里是项目文件的 link:

Swift, Objective C

正如您将看到的,这两个项目都将 运行 在 iPhone X 上完美!

我知道,以下听起来很奇怪。但是尝试使目标图像比您正在绘制的图像大一个像素。这为我解决了(我的特殊问题:)。

在代码中:

UIGraphicsBeginImageContextWithOptions(
    CGSizeMake(viewToSave.image.size.width + 1, 
               viewToSave.image.size.height + 1), 
    viewToSave.opaque, 
    1.0
);

一如既往,答案在于最小的(不包括在问题中)细节......

我真正没有考虑的(并且在相当长一段时间后发现)是 iPhone X 的比例因子等于 @3x。这是 iPhone X 和所有其他设备之间的区别 运行 代码很好...

在我的代码中,我将子视图的 .contentScaleFactor 设置为等于 [UIScreen mainScreen].scale。这意味着在更高端的设备上,图像质量应该更好。

对于 iPhone X,[UIScreen mainScreen].scale returns 3。 对于我测试过的所有其他设备,[UIScreen mainScreen].scale returns 2。这意味着在 iPhone X 上,用于渲染图像的内存量要高得多。

第二个有趣的事实:从 another useful SO post,我发现在 iPhone X 上,如果您尝试分配超过其总内存量 (1392 MB) 的 50%,它崩溃了。另一方面,例如,在 iPhone 6s 的情况下,百分比更高:68% (1396 MB)。 这意味着对于一些较旧的设备,您可以使用比 iPhone X 上更多的内存。

抱歉造成误导,这是我的无心之过。谢谢大家的回答!