I have stumbled on a weird thing. It looks like UIView
's contentScaleFactor
is always 1, even on Retina devices, unless you implement drawRect:
. Consider this code:
@interface MyView : UIView
@end
@implementation MyView
- (id) initWithFrame: (CGRect) frame
{
self = [super initWithFrame: frame];
if (self) {
NSLog(@"%s %g %g %g", __PRETTY_FUNCTION__, self.contentScaleFactor, self.layer.contentsScale, [UIScreen mainScreen].scale);
}
return self;
}
- (void) didMoveToWindow
{
if (self.window)
NSLog(@"%s %g %g %g", __PRETTY_FUNCTION__, self.contentScaleFactor, self.layer.contentsScale, [UIScreen mainScreen].scale);
}
@end
On a Retina device it prints the following:
-[MyView initWithFrame:] 1 1 2
-[MyView didMoveToWindow] 1 1 2
If I add an empty implementation of drawRect:
like this:
- (void) drawRect: (CGRect) rect
{
}
it works as expected:
-[MyView initWithFrame:] 2 2 2
-[MyView didMoveToWindow] 2 2 2
So it looks like it doesn't really matter if the view is in any view hierarchy and what kind of screen it is displayed on. The only thing that does matter is if the view implements drawRect:
or not.
Is that a bug or a feature? I know I can change didMoveToWindow
as below to fix it
- (void) didMoveToWindow
{
if (self.window)
self.contentScaleFactor = self.window.screen.scale;
}
but the default behavior still bugs me.
You may ask why I need contentScaleFactor
at all if I don't draw anything. That's because I just set self.layer.contents
to a ready-made image and then stretch the image with contentStretch
. However, the image doesn't stretch properly on Retina devices unless contentScaleFactor
is set correctly, even though a @2x
image is used. To be precise, it works correctly unless a @2x
image is used. This is, I guess, a bug.
Can anyone share your insight into why contentScaleFactor
behaves this way? Is it specific to iOS 5 only?
Presumably, if you don't override drawRect:
then UIKit knows that a UIView
doesn't draw anything so it takes the (presumably) fast case of having a layer that has a content scale of 1. As soon as you override drawRect:
though, it knows it needs to set up a layer that is of the correct content scale that you can draw into if you want to. It doesn't know that you do nothing in drawRect:
though so it can't make the same assumption as before.
In fact all that is alluded to in the docs:
For views that implement a custom drawRect: method and are associated with a window, the default value for this property is the scale factor associated with the screen currently displaying the view.
Why don't you just override drawRect:
and in that, draw your image? Or you could probably get away with what you're currently doing and have a stub drawRect:
. Given what the docs say, I'd say that's perfectly reasonable to assume it's going to continue to work and is correct behaviour.