Я переношу некоторый код CGContext
в UIGraphicsImageRenderer
для рендеринга изображения.
Новый код оборачивает старый следующим образом:
let renderer = UIGraphicsImageRenderer(size: size)
let image = renderer.image { imageContext in
let cgContext = imageContext.cgContext
// Here goes the old code
let layer = CGLayer(cgContext, size: size, auxiliaryInfo: nil)!
let layerCGContext = layer.context!
// Do some drawing operations (just an example, much more complex in reality)
cgContext.fill([CGRect(x: 100, y: 100, width: 400, height: 200)])
layerCGContext.fill([CGRect(x: 150, y: 150, width: 200, height: 400)])
// Combine the layer on top of the CGContext layer
cgContext.draw(layer, at: .zero) // <- Code breaks here [1]
}
Приведенный выше код прерывается на [1], если что-то было нарисовано в layerCGContext
(пустой слой не вызывает ошибку). Если я удалю эту строку, остальная часть кода будет работать, изображение будет создано, но без дополнительного слоя.
Как я могу создать несколько слоев и объединить их в замыкании UIGraphicsImageRenderer.image{}
?
Похоже, что UIGraphicsImageRenderer
не поддерживает отрисовку CGLayer
в контексте изображения (он выходит из строя). Даже если вы обернете это в реализацию draw
UIView
, а затем попробуете view.drawHierarchy
, это позволит избежать сбоя, но экземпляры CGLayer
не визуализируются изнутри UIGraphicsImageRenderer
(!).
Кажется, это достойно отчета об ошибке, хотя в ближайшее время это вам не поможет. Единственный обходной путь, который я вижу, — использовать устаревшие UIGraphicsBeginImageContext
, UIGraphicsGetImageFromCurrentImageContext
и UIGraphicsEndImageContext
. Подход CGLayer
работает при использовании устаревшего API.
UIGraphicsBeginImageContext(size)
defer { UIGraphicsEndImageContext() }
guard
let cgContext = UIGraphicsGetCurrentContext(),
let layer = CGLayer(cgContext, size: size, auxiliaryInfo: nil),
let layerContext = layer.context
else {
print("unable to get contexts")
return
}
layerContext.setFillColor(UIColor.blue.cgColor)
layerContext.fill([CGRect(x: 150, y: 150, width: 200, height: 400)])
cgContext.draw(layer, at: .zero)
let image = UIGraphicsGetImageFromCurrentImageContext()
Другая альтернатива (которую, как я полагаю, нелегко рассмотреть в вашей конкретной ситуации) — отказаться от использования CGLayer
и просто визуализировать графику напрямую.
Например, если вы хотите визуализировать эти два прямоугольника, вы можете просто fill
на соответствующих экземплярах UIBezierPath
:
let size = CGSize(width: 600, height: 600)
let image = UIGraphicsImageRenderer(size: size).image { _ in
UIColor.red.setFill()
UIBezierPath(rect: CGRect(x: 100, y: 100, width: 400, height: 200))
.fill()
UIColor.blue.setFill()
UIBezierPath(rect: CGRect(x: 150, y: 150, width: 200, height: 400))
.fill()
}
Или, если вы хотите использовать CoreGraphics, вы должны получить новый CGContext
туда, где вы визуализируете эти прямоугольники, а затем вызвать метод fill
:
let image = UIGraphicsImageRenderer(size: size).image { context in
let cgContext = context.cgContext
cgContext.setFillColor(UIColor.red.cgColor)
cgContext.fill([CGRect(x: 100, y: 100, width: 400, height: 200)])
cgContext.setFillColor(UIColor.blue.cgColor)
cgContext.fill([CGRect(x: 150, y: 150, width: 200, height: 400)])
}
Старый код просто использовал
let cgContext = UIGraphicsGetCurrentContext()!
и выполнял рендеринг непосредственно вUIView
. Новый код должен выполнять тот же рисунок, но в виде изображения.