"[CALayer renderInContext]" вылетает на iPhone X
У меня есть обычай UIView
что я хочу сделать как UIImage
, Обычай UIView
это подкласс UIImageView
,
В этом представлении я рендерил некоторые элементы пользовательского интерфейса (рисуя несколько кружков над изображением). Количество добавленных кругов может доходить до тысяч.
Я использую этот простой фрагмент кода, чтобы сделать представление как 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 и в конечном итоге вылетает приложение...
К сожалению, у меня нет доступа к iPhone X, и человек, который сообщил об этом, не имеет доступа к Mac, поэтому я не могу исследовать его глубже.
Я действительно не знаю, делаю ли я что-то не так... Знаете ли вы, есть ли что-то другое в iPhone X? Я долго боролся с этой проблемой...
4 ответа
Как всегда, ответ лежит в мельчайших (и не включенных в вопрос) деталях...
Что я действительно не учел (и узнал через некоторое время), так это то, что iPhone X имеет масштабный коэффициент, равный @ 3x. В этом разница между iPhone X и всеми другими устройствами, на которых выполнялся код...
В какой-то момент в моем коде я настраивал .contentScaleFactor
из подпредставлений, чтобы быть равным [UIScreen mainScreen].scale
, Это означает, что на более дорогих устройствах качество изображения должно быть лучше.
Для iPhone X, [UIScreen mainScreen].scale
возвращает 3. Для всех других устройств, которые я тестировал, [UIScreen mainScreen].scale
возвращает 2. Это означает, что на iPhone X объем памяти, используемый для рендеринга изображения, намного выше.
Забавный факт номер два: из другого полезного поста SO я узнал, что на iPhone X, если вы попытаетесь выделить более 50% его общего объема памяти (1392 МБ), он вылетает. С другой стороны, например, в случае iPhone 6s этот процент выше: 68% (1396 МБ). Это означает, что для некоторых старых устройств у вас есть больше возможностей для работы, чем на iPhone X.
Извините за ошибку, это была честная ошибка с моей стороны. Спасибо всем за ваши ответы!
Я думаю, что проблема связана с тем, как CALayer:renderInContext:
обрабатывает рисование тысяч видов в контексте, который требует их увеличения. Можно ли попробовать отрисовать подвиды самостоятельно? Затем сравните и проверьте, работает ли он лучше, используя контрольно-измерительные приборы.
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;
}
Я знаю, следующее звучит странно. Но попробуйте сделать целевое изображение на один пиксель больше того, которое вы рисуете. Это решило это для меня (моя конкретная проблема: "[CALayer renderInContext]" вылетает на iPhone X).
В коде:
UIGraphicsBeginImageContextWithOptions(
CGSizeMake(viewToSave.image.size.width + 1,
viewToSave.image.size.height + 1),
viewToSave.opaque,
1.0
);
Недавно я написал метод в приложении, которое я делаю, чтобы конвертировать UIView в UIImages (чтобы я мог отображать градиенты на экранах прогресса / панелях вкладок). Я остановился на следующем коде, я использую этот код для рендеринга кнопок панели вкладок, он работает на всех устройствах, включая X.
Цель C:
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:gradientView.bounds.size];
UIImage *gradientImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
[gradientView drawViewHierarchyInRect:gradientView.bounds afterScreenUpdates:true];
}];
Свифт 4:
let renderer = UIGraphicsImageRenderer(size: gradientView.bounds.size)
let image = renderer.image { ctx in
gradientView.drawHierarchy(in: gradientView.bounds, afterScreenUpdates: true)
}
Я использовал этот код в примере проекта, который я написал, вот ссылка на файлы проекта:
как вы увидите, оба проекта будут отлично работать на iPhone X!