Blog Archives

Drawing Retina Graphics

Since the arrival of the iPhone 4, developers need to put two versions of each image into their apps: a low-resolution “Image.png” for all previous iPhone models and a high-resolution “Image@2x.png” for the iPhone 4′s Retina screen.

There are several strategies for creating these higher-resolution images:

  1. Draw the image using vectors at 1x scale and scale up 200% to get the Retina image.
  2. Draw the image using vectors at 2x scale and scale down 50% to get the low-res image.
  3. Draw the image using pixels at 2x scale and scale down. You can also combine pixels and vectors.
  4. Draw the 1x and 2x images separately, either as vectors or bitmaps.

Ideally, you’d use vectors for everything because they scale better than bitmaps. Vector graphics can scale up or down without quality loss. However, if your image includes a texture background then you’ll need to use a bitmap anyway. Just never scale bitmaps up, only down. Continue reading…

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.

Design Idea: A Better Touch Screen

I think tablets such as the iPad are great! They are getting really close to replacing desktop computers and laptops for a lot of people. I expect that in a year or two, many home users will not upgrade to a new PC but will get a tablet instead.

The average user browses the internet, sends email, and occasionally writes something in Word. The iPad is already capable of handling these tasks and many more; it just needs to be able to print wirelessly to any printer and you’re all set.

The iPhone and iPad have proven that touch as the primary mode of interaction works wonderfully well. The problem with today’s touch screens, however, is that there really isn’t much to touch. They are completely flat and smooth. Our fingers deserve more than that!

iPad with raised buttons
I imagine a touch screen where the outer glass layer consists of a matrix of squares that can be configured, electronically, to go up and down. This layer allows the programmer to create a terrain of bumps and holes on top of the pixels that make up the visual display.

When the app shows an on-screen keyboard, for example, it will also send signals to this tactile feedback layer to form actual button shapes on top of the pixel buttons. Tap one of those button shapes and the software will make it sink into the screen, giving the user the impression that he is tapping an actual button. If the refresh speed is quick enough, say 1/10th of a second, then this effect will be believable to the user.

Not every pixel needs to have a “bump”. I’m thinking that one bump per group of 4 pixels will be sufficient to make this work, with each bump having three possible positions: flat, raised or lowered. The raised position would be half a millimeter or so above the surface. Likewise, the lowered position would sink that same distance into the screen.

Seen from the side, it might look something like this:
Diagram of how the tactile layer worksMaybe if this technology becomes sufficiently advanced, several heights could be possible instead of just raised/flat/lowered. Ideally, there would be at least one bump per pixel. With this kind of configuration, the feel of real textures such as wood can be emulated. If the refresh speed is quick enough, even moving things such as a vibrating guitar string could be reproduced. Blind people could read braille straight from the device.

Of course this is wishful thinking. I doubt the technology to do anything remotely like this exists today and I don’t have a clue how one would go ahead and build it. Nanotechnology maybe — you don’t want any wires to be visible on top of the display — but that’s something of the far-away future. Also, because the glass is now no longer flat, care would need to be taken that the underlying picture does not get distorted.

Still, it’s fun to think of these things. Anyone already working on tech like this? ;)

MHNibTableViewCell, Easily Load Table View Cells from NIB Files

Warning: This is an old class that is only useful on iOS 4 or lower. These days you should use UITableView’s new registerNib:forCellReuseIdentifier: method instead, or even easier, use storyboards with prototype cells.

* * *

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…

MHRotaryKnob, a rotary knob control for iOS

Music software often has knobs that you can turn to set the volume or panning. A few days ago I wrote MHRotaryKnob, a UIControl that lets you do the same on the iPhone or iPad. Some rotary knobs can turn forever (such as the old iPod wheel) but this one has a minimum and a maximum, so it can only go round once.

MHRotaryKnob example

This control is open source. You can download it from my Github page.

In this blog post I will explain what goes into writing your own controls. I’ll use my implementation of this rotary knob as an example. Continue reading…