iOS 5 By Tutorials ebook

Blog Archives

Transparent JPEG Images

When you distribute images with your app you usually pick the PNG or JPEG format. The advantage of JPEG is that it often compresses better — especially for photos — but unlike PNG it unfortunately does not support transparency.

The transparency in a PNG file comes from the so-called “alpha channel”. For every pixel not only red, green and blue values are stored but also an “alpha” value that determines how transparent that pixel is. A value of 255 means this pixel is fully opaque, 0 is fully transparent, and anything in between will mix the pixel’s RGB values with the underlying color.

This is probably an old trick, but by saving a JPEG image not as one but as two image files you can still have transparent images. The first image is the regular JPEG with as much compression as you can get away with, the second image is the alpha channel. This is a grayscale image with black representing fully transparent, white fully opaque, and gray everything in between.

 

Source image and its alpha channel
We can combine these two images at runtime to make the image transparent again. Because of the JPEG compression we lose a little bit of clarity but if you tweak the compression settings you can usually get away with it.

I wrote a simple category on UIImage that lets you do this.

Preparing your images

1) Export your image from Photoshop as a PNG with transparency.

2) Export the image again as a JPEG, using suitable compression settings. The background should have a solid color, typically white or black but any color will do.

3) Save the alpha channel to a separate JPEG or PNG image. I couldn’t find an easy way to do this from Photoshop, but the ImageMagick tool can do it without problems.

If you have ImageMagick installed, open a Terminal and go to the folder that contains the exported PNG image. Then type:

convert -alpha Extract -type optimize -strip -quality 60 +dither \
    Source.png Alpha.jpg

This extracts the alpha channel from the PNG image and saves it as a JPEG file. You can tweak the level of compression with the -quality parameter. If you specify “Alpha.png” instead of “Alpha.jpg”, ImageMagick saves the alpha channel as a grayscale PNG-8 file. You should use whichever one makes the smallest file size.

Combining the images

1) Add the two JPEG images (the non-transparent source image and the alpha channel) to the app.

2) At some point, call the mh_combineWithAlphaImage:backgroundColor: method to combine these two images into a new, transparent, image.

3) Depending on your app you may want to do this only once and then store the transparent image as a PNG in your app’s Caches folder.

That’s it, quite easy.

Some notes

The alpha image does not have to be a JPEG, it can also be a PNG file. If it is a JPEG then it can have different quality/compression settings from the main image.

The current implementation works well but is not as fast as it could be. It also uses more memory than is strictly necessary. I might rewrite this at some point to use the Accelerate framework or Core Image.

Not all images compress better as JPEG. You should use JPEG only where it makes sense.

Check out the source code at Github.

Bird image by Sias van Schalkwyk

 

MHTabBarController – A custom tab bar for iOS 5 using the new container APIs

Creating your own container view controllers, i.e. view controllers that can contain other view controllers, used to be a pain but now iOS 5 makes it a lot easier.

Especially for iPad apps that is great, because all of that screen space means you probably want to divide it up amongst more than one view controller.

Today I was messing around with these new container APIs and built my own tab bar controller. It works just like a regular UITabBarController, except the tabs look quite different and the pages slide in and out of the screen with a nice animation.

Screenshot of MHTabBarController in the demo app
If you want to learn more about how to do this, then check out the source code or get the iOS 5 by Tutorials ebook. It has a full chapter on these new view controller containment APIs, plus twenty or so other chapters on more iOS 5 goodness!

MHLazyTableImages – Efficiently Load Images for Large Tables

The iOS Developer Library has a sample project named LazyTableImages that demonstrates how to load images asynchronously for a table with many rows.

The app first downloads the list of top paid apps from iTunes (this is an XML file) and shows the list of apps in a UITableView. Then it downloads the icons for the apps in the background and displays them as they become available. Until the icon is loaded, a placeholder image is visible instead.

Only the icons for the visible rows are downloaded; when you scroll the table to make new rows visible, it downloads the icons for these new rows (but only when you are done scrolling).

Screenshot of the MHLazyTableImages demo project

For a recent client project, I needed to do something similar on several different screens. I took the code from this sample project and rewrote it so it was more generic and could be reused among my different view controllers. Today I decided to refactor this some more and make the code available as an open source component. That component is named MHLazyTableImages and you can download it at github.

To demonstrate how to use this class, I modified Apple’s original LazyTableImages sample. The logic for downloading the images is now handled by the MHLazyTableImages and MHImageCache classes. The table view controller only has to create an instance of MHLazyTableImages and connect its data model and table view to it.

To put an image into a table view cell, you simply call -addLazyImageForCell:withIndexPath:. This will first see if the image is already present in the cache and will download it if not.

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
	UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:...];
	if (cell == nil)
		cell = [[[UITableViewCell alloc] initWithStyle:...];
 
	cell.textLabel.text = ...;
	[lazyImages addLazyImageForCell:cell withIndexPath:indexPath];
}

Of course, you need to tell MHLazyTableImages about the URL for the image. That happens in a delegate callback method:

- (NSURL*)lazyImageURLForIndexPath:(NSIndexPath*)indexPath
{
	AppRecord* appRecord = [self.entries objectAtIndex:indexPath.row];
	return [NSURL URLWithString:appRecord.imageURLString];
}

I used a delegate — rather than telling MHLazyTableImages directly what the URL should be for the cell — in order to accommodate scrolling. While scrolling, we don’t want the images to load yet. We will defer downloading until the user stops scrolling. What that happens, -lazyImageURLForIndexPath: is automatically called for the newly visible rows.

There are a few more things that need to happen to make it all work, but that is the gist of it.

Just for fun, I also replaced the original networking code with ASIHTTPRequest. I was already using this class in MHImageCache, and it allowed me to use blocks instead of delegates. That means the demo project will work only on OS 4 and up.

Check out the demo project (RootViewController in particular) to see how everything works in detail.

MHNibTableViewCell, Easily Load Table View Cells from NIB Files

Most iOS projects use a table view somewhere. Often the standard table view cell types are sufficient to present your data, but sometimes you need more complex cells. You can do this programmatically but I prefer to use Interface Builder.

After typing the same nib loading code over and over, I decided to abstract it into a class, MHNibTableViewCell. This class makes it very easy to use table view cells that are completely designed in Interface Builder.

Continue reading…

SoundBankPlayer: Using OpenAL to Play Musical Instruments in Your iOS App

Suppose you want your iPhone or iPad app to play all the notes from a certain instrument, such as a piano. There are 88 unique notes on a piano so you could sample each note of a real piano and put those 88 sound samples in your app. That’s exactly what some iPhone piano apps do; you can see this for yourself when you examine their application bundles.

For many apps, sampling every note that an instrument can produce is overkill and it will make the application bundle unnecessarily large, especially if you have more than one instrument.

One solution to this problem is the use of SoundFonts. The SoundFont format was originally created by Creative Labs for the Sound Blaster range of sound cards. Instead of requiring a unique sample for each note, the SoundFont allows a single sample to play a range of notes. Now it’s possible to create a realistic piano sound with 8 samples instead of 88.

SoundFonts Simplified

For my Reverse Chord Finder Pro app, I needed a way to play notes from multiple instruments without making the app’s download much larger. I found some good piano and guitar SoundFont files but I did not want to write a complete SoundFont implementation. Instead, I wrote a simple class, SoundBankPlayer, that works in a similar fashion but does not have all the features of a full SoundFont player.

SoundBankPlayer doesn’t read actual SoundFont files but “sound banks”, which are simply PLIST files that describe which notes should be played by which sound samples. This makes it very easy to create new sound banks.

In this article I will explain how to use SoundBankPlayer and how it works internally. I will touch briefly on OpenAL because that is what SoundBankPlayer uses to play back the sounds. Continue reading…