Gesture recognition on the iPhone

Inspired by this detailed article by Carl D. Worth I began experimenting with stroke recognition on the iPhone. Unfortunately the sources for xstroke are very difficult to find nowadays and are unsupported. I finally did find them but I did not want to port all that X11 stuff, so I decided to start from scratch and did a small feasibility study which I want to show you here.

I created the project “KrikelKrakel” (German for scribbling) on BitBucket.

The most interesting class you would look at is KrikelKrakelView. It inherits from UIView and does all the tracking and recognition. The gestures are recognized when the touches ended. The area where touches took place is divided in a grid with 9 cells and the path the finger took is then described by the cell ids. You should have a look at the article mentioned earlier about the details.

One can register as a delegate to get called on different occassions:

- (void) willDrawGesture;

This will be called right inside the touchesBegan method.

- (void) didLearnNewGesture:(NSString*)text;

When a gesture has not been recognized the user will be presented with an alert box where he can enter a letter or some more text.

- (void) didRecognizeGesture:(NSString*)text;

When a gesture has been recognized this method will be called and the stored letter/text will be delivered in the variable text.

The learned gestures a stored in the application documents directory with the name “strokes.dict”. If there is no file on first start the bundled strokes.dict will be used a the initial version.

See a demo video here.

Advertisements

Using UITableViewCell with InterfaceBuilder

The code examples for the iPhone SDK show only how to construct your table cells programatically. If you want to use InterfaceBuilder to make a proper layout which might also be resizable etc. you can use this ViewFactory to handle the creation and reusage of your table cells.

First create a new empty Interface Builder file. Design all the UITableViewCells you want, but they must be root objects so they can be found by the ViewFactory. Each cell MUST have its “identifier” field set. It is in fact its reuseIdentifier required to make a proper recycling of your cell possible.

bild-1

Now create the ViewFactory, the header ViewFactory.h

@interface ViewFactory : NSObject {
    NSMutableDictionary * viewTemplateStore;
}

- (id) initWithNib: (NSString*)aNibName;

- (UITableViewCell*)cellOfKind: (NSString*)theCellKind forTable: (UITableView*)aTableView;

@end

and the implementation ViewFactory.m

#import "ViewFactory.h"

@implementation ViewFactory

- (id) initWithNib: (NSString*)aNibName
{
    if (self == [super init]) {
        viewTemplateStore = [[NSMutableDictionary alloc] init];
        NSArray * templates = [[NSBundle mainBundle] loadNibNamed:aNibName owner:self options:nil];
        for (id template in templates) {
            if ([template isKindOfClass:[UITableViewCell class]]) {
                UITableViewCell * cellTemplate = (UITableViewCell *)template;
                NSString * key = cellTemplate.reuseIdentifier;
                if (key) {
                    [viewTemplateStore setObject:[NSKeyedArchiver
                                                  archivedDataWithRootObject:template]
                                          forKey:key];
                } else {
                    @throw [NSException exceptionWithName:@"Unknown cell"
                                                   reason:@"Cell has no reuseIdentifier"
                                                 userInfo:nil];
                }
            }
        }
    }

    return self;
}

- (void) dealloc
{
    [viewTemplateStore release];
    [super dealloc];
}

- (UITableViewCell*)cellOfKind: (NSString*)theCellKind forTable: (UITableView*)aTableView
{
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:theCellKind];

    if (!cell) {
        NSData * cellData = [viewTemplateStore objectForKey:theCellKind];
        if (cellData) {
            cell = [NSKeyedUnarchiver unarchiveObjectWithData:cellData];
        } else {
            NSLog(@"Don't know nothing about cell of kind %@", theCellKind);
        }
    }

    return cell;
}

@end

You have to make the ViewFactory instance available for your TableViews as a Singleton.

Whenever you want to fill you table cell in your UITableViewDataSource put this line in your method

- (UITableViewCell *)tableView: (UITableView *)aTableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
   UITableViewCell * cell = [viewFactory cellOfKind:@"news" forTable:aTableView];
   // Access the views using [cell viewWithTag:X] in the old way
   return cell;
}

where “news” in this example is the value of the reuseIdentifier.

Internally I store each instance of the UITableViewCells as a template in a NSData archived format and make it accessible via its reuseIdentifier. So whenever cellOfKind:forTable: is called it looks into the reuseQueue of the given table if there is already an old table cell which can be reused or if it needs to instantiate a new one using the archived template to create the new table view cell.

This technique is much more flexible if you are using tables and I hope you will have fun using it.

Quick iPhone Dev Tip: Creating an UIColor with just one RGB value

Are you also tired of splitting that long RGB value into red, green and blue and converting them to a format that UIColor understands? I was, so I wrote a macro that converts the value on compile time and returns an UIColor autorelease object with the correct values:

#define UIColorFromRGB(rgbValue) [UIColor \
 colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

In your code you can use it like this:

UIColor myGreen = UIColorFromRGB(0x00FF00);

Put this in your global header referenced from <project_name>_Prefix.pch and you can use it everywhere in your project without importing it excplicitly.

My first iPhone App has been released!

Yesterday evening I got a mail from Apple that they released my iPhone App WeFind. It took them 8 days (submitted on 30th September) to test it.

It’s a news aggregator which collects its data from our newssite Newsexpress. In the next version there will be other search engines included and many more features are already in the pipeline.

The direct AppStore link is here. Its only available in Germany, Switzerland and Austria since its based on German newspapers.

I am very excited.

News Aggregator

News Aggregator

iPhone 3G – Will it bounce?

I was one of the last lucky people in Rostock in northern Germany to get an iPhone 3G 16 GB black. I also was one of the first iPhone 2G phones in Berlin. But my luck was used up this Friday after only 8 days with my new gadget. While leaning forward to help my 5 year old out of his pants in a public toilet it slipped out of my jacket and crashed onto the tiled floor. The screen cracked:

My, oh my.

My, oh my.

Although the glass is broken the device works perfectly. Even the touch works with the cracks. Yesterday I went to a T-Mobile shop and the iPhone is now on its long way to the T-Mobile central repair station. I guess that will be very expensive. Apple states 299,- € for a 16 GB iPhone 3G. 😦

Fortunately my wife was so kind to lend me old iPhone while I am waiting for the return of my latest toy.

Already ordered a leather case at Amazon and T-Mobile also offers a insurance for broken devices. I will definitely need one.