0

I'm trying to create image-snapshot tests for UIView. Unfortunately for me my CI machines have @1x pixel-to-point ratio, and my local machine has @2x, so basically I'm trying to render a UIView on @1x machine as it would look on @2x machine.

My code looks like this:

let contentsScale = 2

view.contentScaleFactor = contentsScale
view.layer.contentsScale = contentsScale

let format = UIGraphicsImageRendererFormat()
format.scale = contentsScale
        
let renderer = UIGraphicsImageRenderer(size: bounds.size, format: format)
let image = renderer.image { ctx in
    self.drawHierarchy(in: bounds, afterScreenUpdates: true)
}

So problem is that when it reaches CALayer.draw(in ctx: CGContext) inside of drawHierarchy, the view.contentScaleFactor and view.layer.contentsScale are back to 1 (or whatever UIScreen.main.scale is). It happens in this callstack:

* MyUIView.contentScaleFactor
* _UIDrawViewRectAfterCommit
* closure #1 in convertViewToImage

I also noticed that there is ; _moveViewToTemporaryWindow in assembly code of _UIDrawViewRectAfterCommit call, which I guess it means it attaches my view to some temporary window which resets the scale. I tried changing the scale again in didMoveToWindow, i.e. right before the drawing, but the view comes out as pixelated even if view.contentScaleFactor is correct in the rendering of of the layer.

I noticed that some people try to solve it with using scaling on CGContext, but it makes no sense as the underlying quality is not scaled.

So what am I missing? How do render UIView into an image using desired scale?

Paulius Liekis
  • 1,676
  • 3
  • 18
  • 26
  • This may work for you: https://stackoverflow.com/a/51944513/6257435 -- quick test, using a view with a subview, which contains a label as its subview ... seems to work. – DonMag Apr 08 '22 at 16:59

1 Answers1

0

I did several things to get this working:

  • Render layer instead of the view itself (view.layer.render(in: ctx.cgContext) as suggested here: https://stackoverflow.com/a/51944513/6257435
  • Views should have a size that is multiple of your contentsScale otherwise you get weird antialiasing and interpolation issues on the lines.
  • Avoid transforms that have odd scales (like 1.00078... in my case) otherwise you get weird antialiasing and interpolation issues on the lines.
  • I'm using format.preferredRange = .standard color range to make it work the same locally and on CI too.
Paulius Liekis
  • 1,676
  • 3
  • 18
  • 26