UIImage: Tips and Tricks for iOS

When writing apps for iOS its important to know how to deal with images. Whether it's displaying large images in a photo viewer type situation or as simple as an icon for a tab bar item. In this post i'm going to walk through a bunch of tips and tricks for a range of different use cases with images. 

Loading Images: from within your project

UIImage* img = [UIImage imageNamed:@"image.png"];

The imageNamed method, is actually really powerful. For the simplest case, all you need to do is include an image with that exact name somewhere in your project, and thats it.

However, in today's world, there are a few form factors that you may need to support. To display different images  depending if the device is an iphone vs ipad or retina vs non retina would be a pain if you had to write code to detect the device you're currently on. Luckily, Apple does this for you. Using the exact same code as above, you can detect all those cases without writing a single line of code. All you have to do is follow the naming convention below: 

Retina: image@2px.png

iPhone: image~iphone.png

iPad: image~ipad.png

Retina iPhone: image@2px~iphone.png

Retina iPad: image@2px~ipad.png

And thats it. You would still just reference the image with the name "image.png" and the iOS framework will do the rest. Pretty neat, right?

Loading Images: from a url

NSURL* url = [NSURL URLWithString:@"http://www.infragistics.com/assets/images/logo.png"];
NSData* data = [NSData dataWithContentsOfURL:url];
UIImage* img = [UIImage imageWithData:data];

Depending on your app, its very possible that you'll need to load images from a url. For example, if you're writing an app that connects to Netflix or Twitter, you'd need to pull the images from a URL. To do this, first you'll need to download the images. You can do this by using the NSData dataWithContentsOfURL method. You'll then take the output from that method and use the UIImage imageWithData method. 

Loading Images: from a directory

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString* fullPath = [[paths objectAtIndex:0] stringByAppendingPathExtension:@"img.png"];

UIImage* img = [UIImage imageWithContentsOfFile:fullPath];

This example assumes that you've already saved an image called "img.png" in the NSLibraryDirectory of your app. However, the point is to show you, that given a file path, you can use a predefined method on the UIImage class to load an image. 

Saving Images: from NSData

NSURL* url = [NSURL URLWithString:@"http://www.infragistics.com/assets/images/logo.png"];
NSData* data = [NSData dataWithContentsOfURL:url];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString* fullPath = [[paths objectAtIndex:0] stringByAppendingPathExtension:@"img.png"];

[data writeToFile:fullPath atomically:YES];

Previously, i showed you how to load images from a url and from a file structure. Here, we're downloading an image from a url, and we're going to save it, so that we can load it later, essentially cacheing the image for quicker access later. 

Saving Images: from UIImage (PNG)

UIImage* img = _imageView.image;
NSData* data = UIImagePNGRepresentation(img);
[data writeToFile:fullPath atomically:YES];

In this example, we only have access to a UIImage. However, we want to take that image and save it for later use. To do that, first we need to convert that image to an NSData object first. Then we can save it like we normally save images. iOS has 2 methods that make this really easy. The first one is to save the image into png format using UIImagePNGRepresentation. Simply call the method and pass in your UIImage object. 

Saving Images: from UIImage (JPEG)

UIImage* img = _imageView.image;
NSData* data = UIImageJPEGRepresentation(img, 1.0);
[data writeToFile:fullPath atomically:YES];

This code, is almost identical, to the PNG version, except the jpeg version offers an additional parameter. You can actually control the quality of the compression. So, if you have a really large image, you can actually lower its quality for ease of use, especially if you're on an older device such as an iPad 1, where memory management becomes a lot more important. 

The compression parameter accepts a float value from 0.0 to 1.0. Where 1.0 is the full quality of the image and anything lower, is a poorer quality version of the image. 

Take a screenshot of a UIView

-(UIImage*)screenShotOf:(UIView*)view
{
   UIGraphicsBeginImageContext(view.frame.size);

   CGContextRef context = UIGraphicsGetCurrentContext();

   [view.layer renderInContext:context];

   UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

   UIGraphicsEndImageContext();

   return img;
}

Using CoreGraphics you can actually take snapshots of any part of your application. This may be useful if you're doing some transitions and it would be too heavy to animate your entire view. Such as a page turning scenario. You can then do what you want with the UIImage thats returned, including saving it using the methods I showed you earlier. 

Note: in order to use the above code, you'll need to include the QuartzCore.framework and add the following import stmt: #import <QuartzCore/QuartzCore.h>

UIImageView ContentModes

To display your images you should use the UIImageView object. When it comes to displaying the image though, you have quite a few options via the contentMode property on the UIImageView class. 

Here's an example of the three scaling options of the contentMode property: 

UIViewContentModeScaleAspectFill

In this mode the image's aspect ratio is maintained, but the entire image isn't being displayed.

UIViewContentModeScaleAspectFit

In this mode, the image's aspect ratio is maintained and the entire image is scaled dow to fit within the give frame.

UIViewContentModeScaleToFill

Finally, in this mode, the image's aspect ratio is ignored and the image is stretched to fit within the bounds of the frame.

Tip: When displaying photos, your best bet is to use UIViewContentModeScaleAspectFit, as it will actually scale your image to fit properly within it's frame. 

Using an image as a color

Did you know that you can take an image, and use it as a UIColor? The reason you would do this, is b/c you have an image that repeats, and instead of creating a larger image for your app, you can just create a snippet of the image and use it as a pattern. 

Using the image above, i would use the following code:

UIImage* img = [UIImage imageNamed:@"pattern.png"];
self.view.backgroundColor = [UIColor colorWithPatternImage:img];

Which will produce the following:

Allow Zooming and Panning on an Image

A very common scenario is to have an Image that you display in an app that can be zoomed and panned by the user. While the UIImageView doesn't support this natively, its actually quite simple to do. You simply have to embed your UIImageView in to a UIScrollView. Take a look at the following code:

@interface ViewController ()< UIScrollViewDelegate >
{
   UIImageView* _imageView;
   UIScrollView* _scrollView;
}
@end

@implementation ViewController

- (void)viewDidLoad
{
   [super viewDidLoad];

   CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
   UIViewAutoresizing mask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;;

   _scrollView = [[UIScrollView alloc]initWithFrame:frame];
   _scrollView.autoresizingMask = mask;
   _scrollView.delegate = self;
   _scrollView.maximumZoomScale = 6;
   [self.view addSubview:_scrollView];

   _imageView = [[UIImageView alloc]initWithFrame:frame];
   _imageView.autoresizingMask = mask;
   [_scrollView addSubview:_imageView];

   _imageView.contentMode = UIViewContentModeScaleAspectFit;

   UIImage* img = [UIImage imageNamed:@"photo1.jpeg"];
   _imageView.image = img;
}

-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
   return _imageView;
}

@end

Its really only a few lines of code. We create a scrollView and put our imageView in the scrollView. Then we set the delegate of the scrollView, and implement one method for the delegate: viewForZoomingInScrollView:. B/c we want to zoom on the our image, we return our imageView. Then the only other thing to do is set the maximumZoomScale for the scrollView to a number larger than 1, and thats it. 

Scale Images: Option 1 (Automatic):

UIImage* img = [UIImage imageNamed:@"photo1.jpeg"];
img = [UIImage imageWithCGImage:img.CGImage scale:2 orientation:UIImageOrientationUp];
_imageView.image = img;

Your first option is to go use the built in mechanism. Simply load your image, pass it in to a static method off of UIImage and set the scale that you want. The scale here will be what the image is divided by. So a scale of 2 will mean that the image is half of its original size. 

Scale Images: Option 2 (Manual):

UIImage* img = [UIImage imageNamed:@"photo1.jpeg"];
img = [self scaleAndResizeImage:img scaledBy:.5];
_imageView.image = img;

-(UIImage*)scaleAndResizeImage:(UIImage*)imageToScale scaledBy:(CGFloat)scaleFactor
{
   CGImageRef img = imageToScale.CGImage;
   float actualHeight = CGImageGetHeight(img);
   float actualWidth = CGImageGetWidth(img);

   float scaledHeight = floorf(actualHeight*scaleFactor);
   float scaledWidth = floorf(actualWidth*scaleFactor);

   CGRect newRect = CGRectIntegral(CGRectMake(0, 0, scaledWidth, scaledHeight));

   CGColorSpaceRef genericColorSpace = CGColorSpaceCreateDeviceRGB();
   CGContextRef bitmap = CGBitmapContextCreate(NULL,
       scaledWidth,
       scaledHeight,
       8, (4 * scaledWidth),
       genericColorSpace,
       kCGImageAlphaPremultipliedFirst);
   CGColorSpaceRelease(genericColorSpace);
   CGContextSetInterpolationQuality(bitmap, kCGInterpolationDefault);

   CGContextDrawImage(bitmap, newRect, img);

   CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
   CGContextRelease(bitmap);

   UIImage *newImage = [UIImage imageWithCGImage:newImageRef];

   CGImageRelease(newImageRef);

   return newImage;
}

This option is to write your own scaling method, which gives you a little bit more control.

Both of these approaches will produce the same exact image, which is useful when you want to create thumbnails of your images when display lots of images at once. 

Conclusion

As you can see there is a lot that you can do with images in iOS. Knowing whats possible will allow you to really create some awesome user experiences for your consumers. 

If this was interesting to you check out my blog for more iOS tips and tricks, and follow me on twitter: @codebystevez

As always, I hope this was helpful. 

By Stephen Zaharuk (SteveZ)


Tags / ,