iPhone SDK: formatting a numeric value with NSNumberFormatter

While working on an application that downloads a PDF document into a UIWebView, I wrote some code to display a progress bar while the user waits for the file to be downloaded. It turned out to be very simple to do this, but I hit a snag after trying to also display the amount of bytes that has been retrieved so far. Something like “0.45 Mb of 4.56 Mb”.

Problem was that by default NSNumberFormatter displays number with 3 decimal places, and I needed to customize that a bit, so I could get only 2 decimal places, and always a number before the decimal separator. For instance, getting “0.45 Mb” instead of just “.45 Mb”. The magical solution was the setPositiveFormat method, which allows the developer to specify these parameters.

Here’s the code that I ended up using, and it works great:

- (void)createProgressionAlertWithMessage:(NSString *)message {
    progressAlert = [[UIAlertView alloc] initWithTitle:message message:@"Please wait..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
    // Create the progress bar and add it to the alert
    progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(30.0f, 80.0f, 225.0f, 90.0f)];
    [progressAlert addSubview:progressView];
    [progressView setProgressViewStyle:UIProgressViewStyleBar];
 
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(90.0f, 90.0f, 225.0f, 40.0f)];
    label.backgroundColor = [UIColor clearColor];
    label.textColor = [UIColor whiteColor];
    label.font = [UIFont systemFontOfSize:12.0f];
    label.text = @"";
    label.tag = kDownloadCounterTag;
    [progressAlert addSubview:label];
 
    [progressAlert show];
    [progressAlert release];
}

That’s the method that creates the UIAlertView that will hold the progress bar, and also the UIProgressView itself to display the progress of the download. I added an extra label in there to finally display the actual bytes of the file as it is being streamed over.

For the actual download I’m using NSURLConnection so I can download the file asynchronously, and receive information while the download is progressing.

- (void)viewWillAppear:(BOOL)animated {
    NSString *file = [NSString stringWithFormat:@"http://domain.com/download?id=%@", self.docID];
    NSURL *fileURL = [NSURL URLWithString:file];
 
    NSURLRequest *req = [NSURLRequest requestWithURL:fileURL];
    NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
 
    [self createProgressionAlertWithMessage:@"Downloading document"];
}
 
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.fileData setLength:0];
    self.totalFileSize = [NSNumber numberWithLongLong:[response expectedContentLength]];
}
 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.fileData appendData:data];
 
    NSNumber *resourceLength = [NSNumber numberWithUnsignedInteger:[self.fileData length]];
    NSNumber *progress = [NSNumber numberWithFloat:([resourceLength floatValue] / [self.totalFileSize floatValue])];
    progressView.progress = [progress floatValue];
 
    const unsigned int bytes = 1024 * 1024;
    UILabel *label = (UILabel *)[progressAlert viewWithTag:kDownloadCounterTag];
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    [formatter setNumberStyle:NSNumberFormatterDecimalStyle];
    [formatter setPositiveFormat:@"##0.00"];
    NSNumber *partial = [NSNumber numberWithFloat:([resourceLength floatValue] / bytes)];
    NSNumber *total = [NSNumber numberWithFloat:([self.totalFileSize floatValue] / bytes)];
    label.text = [NSString stringWithFormat:@"%@ MB of %@ MB", [formatter stringFromNumber:partial], [formatter stringFromNumber:total]];
    [formatter release];
}
 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [progressAlert dismissWithClickedButtonIndex:0 animated:YES];
}

I’m hoping this is useful to someone else. Let me know if there’s a better to do some of the things I’m doing here.

iPhone SDK: Customizing back button title

When developing navigation controller-based apps, it’s pretty common to want to customize the title of the back button that is displayed on the navigation bar. Usually the button title is set to the parent view controller’s title, but you can customize that.

All you need to do is add some code to the viewDidLoad method in the parent view controller:

- (void)viewDidLoad {
    [super viewDidLoad];
 
    self.title = @"Title goes here";
 
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    UIBarButtonItem *syncButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refresh:)];
    self.navigationItem.rightBarButtonItem = syncButton;
    [syncButton release];
 
    UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStyleBordered target:nil action:nil];
    self.navigationItem.backBarButtonItem = backButton;
    [backButton release];
 
    [self refreshData];
}

Screenshots of the parent and child view controllers are available below:

iPhone SDK: Parsing semi-complex JSON objects

Since this seems to be quite common for newcomers to Objective-C / iPhone development, here’s another example on how to parse a semi-complex JSON object. This was asked as a comment on a previous post, but I think that deserves its own space.

The JSON object looks something like the following:

{"start":0,
 "stat":"ok",
 "locations":[{"name":"Pensacola, FL",
               "place_id":"qQ7Vig2bBZsZCy82",
               "woeid":2470377}],
 "count":3,
 "total":3,
 "query":"address=Pensacola"}

So you can quickly see that it’s an object, with a “locations” entry that contains an array of objects. This looks like an object that represents the search results for a location from some web service.

I will parse that complex object by creating separate variables to hold specific parts of the object, but there are simpler ways to do this. In this example, I want to get the value for the “name” key in the first item of the “locations” array.

// jsonString contains the actual JSON output from your web service
SBJSON *json = [[SBJSON alloc] init];
NSError *error = nil;
// object containing full results
NSDictionary *results = [json objectWithString:jsonString error:&error];
// array just for the "location" results
NSArray *locations = [results objectForKey:@"locations"];
// first location in your array
NSDictionary *firstLocation = [locations objectAtIndex:0];
// finally, the name key
NSString *name = [firstLocation objectForKey:@"name"];
[json release];

Rolando: incredible iPhone game

I don’t usually stop here to recommend applications or games for the iPhone, but I feel I need to take some time and do a review of Rolando, a game that had a lot of hype behind it for a few months, and it is now finally available on the App Store.

I have to give it to Simon Oliver, the guy behind this game. It’s an unbelievably well done game, starting with the beautiful (and playful) graphics and in-game story, all the way to the innovations in game control that allow for a great user interaction experience. The sound effects and music for the game are also top notch. I mean, I’m now at a point where I open the game and load a specific level of the game and just listen to the soundtrack.

This game is now the standard bearer for iPhone games, it’s just that good. The $9.99 price is completely deserved, and I recommend it to anyone.

Video trailers and screenshots are available from the Rolando website.

Book review: Beginning iPhone Development

I have been going through this book for the past few weeks, and so far it’s the best book around for developers beginning with Objective-C and the iPhone SDK. The writing is natural (and that’s hard to find these days), detailed and sometimes funny in a quirky way. I appreciate the fact that they use lots of screenshots to explain the interactions with Interface Builder and Xcode.

They have Chapter 3 (Handling Basic Interaction) available for free online if you would like to read a sample of the book. Highly recommended.

You may also like to follow Jeff LaMarche’s blog, one of the co-authors of this book.

« Previous Page« Previous entries « Previous Page · Next Page » Next entries »Next Page »