On the importance of setting contentScaleFactor in CATiledLayer-backed views

If you look at any samples for CATiledLayer (such as ZoomingPDFViewer), you will invariably see code like this:

// to handle the interaction between CATiledLayer and high // resolution screens, we need to manually set the tiling view's // contentScaleFactor to 1.0. (If we omitted this, it would be 2.0 // on high resolution screens, which would cause the CATiledLayer // to ask us for tiles of the wrong scales.)
pdfView.contentScaleFactor = 1.0

Without this line, on retina devices such as iPhone 4/4S or the latest iPad, your view will probably ask for 4x as many tiles as are necessary.  (Note that retina screens have 4x as many pixels as their non-retina counterparts, so unless you are doubling the dimensions of CATiledLayer tileSize for retina, your view will already ask for 4x as many tiles as for non-retina, so in this case your retina view would ask for 16x as many tiles as its non-retina version.)

That’s all well and good.  The point I wanted to make in this post was simply a reminder that if you are doing any view juggling with your CATiledLayer-backed views (such as dequeueing them for reuse as pages in an infinite scroll view), you need to set contentScaleFactor each time you add your view to the view hierarchy.  Otherwise, it will take on the contentScaleFactor of its new superview and it will start asking for the wrong tile sizes.

Update: It’s not just view juggling. Any time your CATiledLayer-backed view or any view higher up in its view hierarchy is added to a parent view hierarchy, then contentScaleFactor of your view will reset to 2 on a retina device.  This includes switching tabs in a UITabBarController or pushing a new view controller onto the navigation stack of a UINavigationController.  The way I’ve handled this is by overriding viewWillAppear: in the UIViewController that contains the CATiledLayer-backed view.

Update 2: Aaron Farnham wrote me to suggest that you simply override didMoveToWindow in your CATiledLayer-backed UIView and call setContentScaleFactor:1 there.  Like so:

- (void)didMoveToWindow {
    self.contentScaleFactor = 1.0;
}

Author: Mark

Mark is an American computer programmer living in Switzerland.