“[CALayer renderInContext]”在 iPhone X 上崩溃
"[CALayer renderInContext]" crashes on iPhone X
我有一个自定义 UIView
,我想将其呈现为 UIImage
。自定义 UIView
是 UIImageView
的子类。
在此视图中,我正在渲染一些 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:
正如您将看到的,这两个项目都将 运行 在 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 上更多的内存。
抱歉造成误导,这是我的无心之过。谢谢大家的回答!
我有一个自定义 UIView
,我想将其呈现为 UIImage
。自定义 UIView
是 UIImageView
的子类。
在此视图中,我正在渲染一些 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:
正如您将看到的,这两个项目都将 运行 在 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 上更多的内存。
抱歉造成误导,这是我的无心之过。谢谢大家的回答!