views:

5011

answers:

4

I've got a standard UITableViewCell where I'm using the text and image properties to display a favicon.ico and a label. For the most part, this works really well since UIImage supports the ICO format. However, some sites (like Amazon.com say) have favicon.icos that make use of the ICO format's ability to store multiple sizes in the same file. Amazon stores four different sizes, all the way up to 48x48.

This results in most images being 16x16 except for a few that come in at 32x32 or 48x48 and make everything look terrible. I have searched here, the official forum, the documentation, and elsewhere without success. I have tried everything that I could think of to constrain the image size. The only thing that worked was an undocumented method, which I'm not about to use. This is my first app and my first experience with Cocoa (came from C#).

In case I wasn't clear in what I'm looking for, ideally the advice would center around setting the dimensions of the UIImage so that the 48x48 version would scale down to 16x16 or a method to tell UIImage to use the 16x16 version present in the ICO file. I don't necessarily need code: just a suggestion of an approach would do me fine.

Does anyone have any suggestions? (I asked in the official forum as well because I've sunk more than a day into this already. If a solution is posted there, I'll put it here as well.)

A: 

I don't know anything about how ico files work, so maybe it's possible to handily extract the 16x16 image and use that, but I have no idea how.

As far as I'm aware, it's not possible to set any useful properties on the image in a generic UITableViewCell. The simplest way (still messy, especially if you're new to it) to scale everything down to 16x16 would be to subclass UITableViewCell, give it a UIImageView, always size the image view to 16x16, and set its contentMode to UIViewContentModeAspectFit.

zem
I tried this approach and it didn't work, but I was probably just doing it wrong.
bbrown
+2  A: 

I'm doing something similar in my app.

    UIImage* resizedImage(UIImage *inImage, CGRect thumbRect)
{
    CGImageRef   imageRef = [inImage CGImage];
    CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);

    if (alphaInfo == kCGImageAlphaNone)
     alphaInfo = kCGImageAlphaNoneSkipLast;

    CGContextRef bitmap = CGBitmapContextCreate (NULL, thumbRect.size.width, thumbRect.size.height, 8, thumbRect.size.width*3, 
                        CGColorSpaceCreateDeviceRGB(), alphaInfo);

    CGContextDrawImage(bitmap, thumbRect, imageRef);

    CGImageRef ref = CGBitmapContextCreateImage(bitmap);
    UIImage* result = [UIImage imageWithCGImage:ref];

    CGContextRelease(bitmap);
    CGImageRelease(ref);

    return result;
}

Then create a new image constraining the proportions. Replacing "image" with the ICO file you get from Amazon.

CGRect sz = CGRectMake(0.0f, 0.0f, 16, 16);
UIImage *resized = resizedImage(image, sz);
Brandon Schlenker
For some reason, your code was throwing a lot of CoreGraphics errors for me. It could be an issue with the ICO format. It's a good answer though.
bbrown
Were they compile/link errors? I don't think Xcode links to the CoreGraphics framework by default for new projects so you'll need to add that and the corresponding @import statement.
Daniel Dickison
Yeah you need to link to the CoreGraphics framework. If you didn't that would be why you're getting those errors.
Brandon Schlenker
Your bytes per row is wrong, as pointed out by crafterm in his commenting answer. See my comment on that answer for details, but you should be multiplying the width by 4, not 3.
Peter Hosey
+6  A: 

Here's what user "bcd" over at the official forums posted that ended up solving the problem (with a slight modification by myself to make it static for inclusion into a set of utility methods):

+ (UIImage *)scale:(UIImage *)image toSize:(CGSize)size
{
    UIGraphicsBeginImageContext(size);
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return scaledImage;
}
bbrown
BCD's version was a category on `UIImage`, which makes so much more sense than what I did here.
bbrown
+1  A: 

Don't have enough karma yet to comment on answers, but I had good success with bbrandons code above.

I did get a fair number of warnings on the console though regarding bytes per row setting not being high enough - after reading answers to this post I ended up setting it to 0, which lets the system determine the right value.

eg:

CGContextRef bitmap = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, CGColorSpaceCreateDeviceRGB(), alphaInfo);
crafterm
If you claimed 8 bits per component, that's 1 byte per component. brandon's answer prevents `kCGImageAlphaNone`, so, for an RGB color space, there are always four components (three color, plus alpha). That's 1×4=4 bytes per pixel. Thus, he and you should be multiplying the width (pixels per row) by 4, not 3. Passing 0 there isn't documented to work, so you probably shouldn't rely on it and should either not use that trick or file a documentation bug asking for it to be documented: https://bugreport.apple.com
Peter Hosey
Thanks for your description of the calculations Peter, makes sense - I'll also file a documentation bug for the automatic calculation as well.
crafterm