Search code examples
objective-cuiviewcalayerzooming

What does CALayer know that I do not when zooming


How can I draw as smoothly as CALayer does while zooming?

As seen in the third (tallest) image the white border is drawn with accurate, anti-aliased rendering.

However neither my labels (even though I set the contentScaleFactor) nor my hand drawn circles (even though I turn anti aliasing on) are.

How can I have anti-aliased rendering for either UIKit components or hand-drawn graphics that scale with the zooming factor as smoothly as what CALayer does?

What's the magic?

small medium large

The white border is drawn using the view's CALayer:

    self.layer.cornerRadius = frame.size.width / 2.0f ;
    self.layer.borderColor = [UIColor whiteColor].CGColor ;
    self.layer.borderWidth = 3.0f ;

The numbers are UILabel's

    CGFloat contentScale = self.zoomScale * [UIScreen mainScreen].scale; // Handle retina
    label.contentScaleFactor = contentScale;

And given:

    typedef struct {
        CGRect      rect ;
        CGColorRef  color ;
    } EventColor ;

the 4 circles are drawn using CoreGraphics

    ::CGContextSetAllowsAntialiasing(context, true) ;
    ::CGContextSetShouldAntialias(context, true) ;
    // ...
    EventColor ec = ...
    // ...
    ::CGContextSetFillColorWithColor(context, ec.color) ;
    ::CGContextFillEllipseInRect(context, ec.rect) ;

Solution

  • It turns out that I misunderstood what contentScaleFactor was meant for, and discovered that what I really was after was CALayer's contentScale.

    The upshot is that the drawRect method in whatever UIView subclass can go ahead and pretend that the zooming scale is always set to 1.0 provided that the view's layer's contentScale is set to reflect the current zoom level of the scroll view.

    - (void) scrollViewDidEndZooming: (UIScrollView *) scrollView
                            withView: (UIView *) view
                             atScale: (float) scale {
    
        scale *= [UIScreen mainScreen].scale ;
        for (UIView * v in view.subviews) {
            v.layer.contentsScale = scale ;
            [v setNeedsDisplay] ;
        }
    }
    

    The example above assumes that the scroll view contains a non opaque container view which in turn contains every actual views whose layer.contentScale needs adjusting.