Alternatives for Supporting All Resolutions in UIKit Apps

I’ve been working hard to get an iPhone/iPod version of my iPad app out. When my business partner got his new iPhone4, we were happy to see that the techniques I used to re-use graphics between the iPad and iPhone worked so well that the app even looks good on the Retina Display.

First off, I understand that the official directions to support high resolutions will work for most people. But my application has currently over 500 images in it, and a few hundred more are being added. So to make a @2x version of each image just seems out of the question for me. So I had used 2 other techniques that I originally developed to share images betwen the iPhone and iPad version that worked with no extra effort on the iPhone 4.

Method 1: Draw scaled down bitmap images using Quartz.

I actually went with Quartz because I needed to draw shadows, which is a subject I’ll return to later. But it turned out that CGContextDrawImage will scale the image into the frame perfectly on all devices. If the image has enough resolution, it even looks sharp on the iPhone4. Since my images all were originally from the iPad, they looked perfectly sharp. I don’t know if everyone’s images will scale so well, but my experience says it’s worth a try.

The problem with using Quartz is that the coordinate system is not the same as UIView. But a couple of transformations, and everything looks fine. I made my own subview of UIView, and drawRect looked something like this:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0 , -1.0);

    CGContextDrawImage(context, self.frame, self.theImage.CGImage);
}

Method 2: Use PDF for Vector Graphics

This turns out to be very powerful and useful. PDF was actually one of the source formats for much of the licensed graphics we used. And since Adobe Illustrator can output PDF as well, it was very easy to make changes or new images. And I was able to use Automator on the Mac to easily use the reduce file size Quartz filter to get the overall download size to under 20 Meg.

PDFs are a little confusing to use, so I also made a UIView subclass to display them. I’m loading them from a file URL in the drawRect code below, and rescaling them proportionally by hand. The code below is reloading the PDF every time it draws. With 7 PDFs on the screen at once, it has a slight pause, otherwise it was fast enough that I never felt like I needed to make it faster. You could certainly preload them, or even draw them into a bitmap if you need to redraw them frequently.

- (void)drawRect:(CGRect)rect {
    if (self.pdfURL != nil) {
        pdfDoc = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
        page = CGPDFDocumentGetPage (pdfDoc, 1);

        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
        CGRect boxRect = CGPDFPageGetBoxRect (page, kCGPDFCropBox);

        float xRatio = self.bounds.size.width/boxRect.size.width;
        float yRatio = self.bounds.size.height/boxRect.size.height;
        float ratio = xRatio < yRatio? xRatio : yRatio;
        CGContextTranslateCTM(context, 0.0, -(self.bounds.size.height-boxRect.size.height*ratio)/2);
        CGContextScaleCTM(context, ratio , -ratio);

        CGContextSaveGState (context);

        CGContextClipToRect (context,boxRect);
        CGContextDrawPDFPage (context, page);
        CGContextRestoreGState (context);
        CGPDFDocumentRelease(pdfDoc);
    }
}

If app size is looking like it might be an issue that makes it hard to support all the different devices, consider if one of these techniques will help.

Advertisements

Leave a comment

Filed under Graphics