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.





