Changing the Navigation Bar: MFMailComposeViewController

Changing the Navigation Bar: MFMailComposeViewController

The iPhone 3.0 SDK introduced a number of cool new controllers, one of which was the MFMailComposeViewController class.

And although – in and of itself – it does so much, like much of what we run into in this developer life, it leaves some things greatly to be desired.

Like, “why in the heck does it ** have ** to put the subject line in the navigation bar as a title? Don’t they ** know ** I have a cool custom nav bar that they’re totally puking over?!?!?!

Don’t fret. There is a solution.

First, you’ll need to implement the following delegate in the .h file of the controller where you are implementing the mail composer:

MFMailComposeViewControllerDelegate

Next, you’ll need to implement some sort of event handler. Here’s mine:

-(IBAction)doEmail:(id)id {
if ([MFMailComposeViewController canSendMail]) {

  MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];

  controller.mailComposeDelegate = self;
  [controller setSubject:@"My Subject"];
  [controller setMessageBody:@"My Body" isHTML:YES];

  [self presentModalViewController:controller animated:YES];

  [[controller navigationBar] setTintColor:[UIColor greenColor]];
  UIImage *image = [UIImage imageNamed: @"custom_nav_background.png"];
  UIImageView * iv = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,320,42)];
  iv.image = image;
  iv.contentMode = UIViewContentModeCenter;
  [[[controller viewControllers] lastObject] navigationItem].titleView = iv;
  [[controller navigationBar] sendSubviewToBack:iv];
  [iv release];
 
  [controller release];
  }
else {
  UIAlertView* myAlert = [[UIAlertView alloc] initWithTitle:@"My App" message:[NSString stringWithFormat:@"You cannot send email at this time."] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
  [myAlert show];
  [myAlert release];
  }
}

Finally, you’ll need to implement a method to dismiss the email composer after you cancel or send.

// Dismiss email composer UI on cancel / send
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
  [self becomeFirstResponder];
  [self dismissModalViewControllerAnimated:YES];
}

The bits that take care of the custom navigation bar (and incidentally, hiding the subject line title Apple insists on fobbing off on you) are in bold in the code above.

There are a few interesting details to note:

  [[controller navigationBar] setTintColor:[UIColor greenColor]];

Will tint the Cancel and Send buttons to whatever color you like; in this example, I chose green. Use whatever you like here.

  UIImage *image = [UIImage imageNamed: @"custom_nav_background.png"];
  UIImageView * iv = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,320,42)];
  iv.image = image;
  iv.contentMode = UIViewContentModeCenter;
  [[[controller viewControllers] lastObject] navigationItem].titleView = iv;
  [[controller navigationBar] sendSubviewToBack:iv];
  [iv release];

The code above loads an navigation bar image (320 x 42 pixels for iPhone 2G, 3G, and 3Gs; 640 x 84 pixels for retina display), and sets the titleView on the MFMailComposerViewController navigation bar to the UIImageView I constructed with my custom navigation bar background.

And last but definitely not least, we must send the custom titleView to the back so that we can “see” the Cancel and Send buttons.

The subject line will now no longer appear in the title bar – which is why I did this in the first place. It was totally jamming my chi.

Give it a try. I’d be interested in your feedback on this solution.

Stop the Bouncy UIWebViews!

Stop the Bouncy UIWebViews!

If you’ve ever used UIWebView inside of any iPhone app, one of the more unique – and some would say, annoying – behaviors of the embedded web view is that it “bounces” when pulled down, exposing a gray background and letting your users know that “hey – this guy is using a web view!

There is an answer to the “bouncy” web view problem. Actually, there are two (2) answers to the “bouncy” web view problem. Since one uses an undocumented call and will therefore get your app “bounced” from the App Store (sorry for the bad pun), I won’t include it here.

The answer that ** won’t ** get your app declined but ** will ** stop the bouncing may be found below:

// Stop the bounces
UIScrollView* sv = nil;
for(UIView* v in myWebView.subviews){
  if([v isKindOfClass:[UIScrollView class] ]){
    sv = (UIScrollView*) v;
    sv.scrollEnabled = YES;
    sv.bounces = NO;
    }
  }

This will still allow your web view to scroll, but won’t “bounce” when you try to pull the view up or down outside of its given bounds. If you also want to stop scrolling, simply change the sv.scrollEnabled line to NO.

Amigo FriendFeed Client iPhone Application – Still Cookin’, But Getting Close

Amigo FriendFeed Client iPhone Application – Still Cookin’, But Getting Close

Work in Progress – Amigo

Work in Progress – Amigo

Cheap Gas!… In Print!

Cheap Gas!… In Print!

Our Cheap Gas! iPhone application will be in a print article in the upcoming June issue of Budget Travel Magazine and in the upcoming July issue of Travel + Leisure Magazine.

I’ll let everyone know when these hit the newsstands.

One never knows how these things will turn out (favorable or not) until you see the articles in black and white.

But hey – I’m just excited to be included in the conversation.  Keeping my fingers crossed for good reviews.

Cheap Gas! Version 2.01 is now available in iTunes

Cheap Gas! Version 2.01 is now available in iTunes

A fix for problems with Refresh and Resort is now available (or will be this evening, at any rate) in iTunes.

Version 2.01 corrects a bug in refresh that was causing the app to get blown out of the water.  Resorting by price and distance has also been stabilized.

Hopefully, this will go a long way toward correcting several irritating issues.

Simple Multiplication Flash Cards

Simple Multiplication Flash Cards

UPDATE: A premium version of Simple Flash Cards has been submitted to the App Store for Approval.  The premium version allows you to switch between addition and multiplication, and allows you to drill specific numbers (0 through 15).

Updates Submitted to Apple for Cheap Gas! and Cheap Gas! Plus

Updates Submitted to Apple for Cheap Gas! and Cheap Gas! Plus

Updates have been submitted this afternoon for both Cheap Gas! and Cheap Gas! Plus to the Applie iTunes App Store for approval.

Both versions have changes for improved location services and improved checking for being under Airport Mode.

Sorry for the delay – have been busy, busy, busy.

iPhone Email Attachments – Revisited

iPhone Email Attachments – Revisited

My solution for sending messages via email on the iPhone – detailed here – works for every mail client… except Gmail.

Gmail simply will not let you see embedded images.  Period.

Since this kinda crap really grates on my nerves, I started digging a little deeper.  Plus, I really think there should be a 100% universal way to attach stuff to email messages from the iPhone… and I’m not the only one.  My embedded images solution is workable, but is not wholly satisfying – because it is not a 100% solution.

So, like I usually do when trying to sniff out how a particular piece of web software works without having access to the source, I see what is being sent “over the wire.”  And since I knew that iPhoto on the iPhone WAS able to send an attachment, I sent myself a picture… and then checked the raw message source to see how Apple was doing it.

I sent myself a picture, with no accompanying text, and this is what the “interesting” pieces looked like:

Content-Type: multipart/mixed;
boundary=Apple-Mail-1-782786827
X-Mailer: iPhone Mail (5H11)
Mime-Version: 1.0 (iPhone Mail 5H11)
Date: Wed, 11 Mar 2009 12:13:43 -0400

–Apple-Mail-1-782786827
Content-Type: text/plain;
charset=us-ascii;
format=flowed
Content-Transfer-Encoding: 7bit

–Apple-Mail-1-782786827
Content-Disposition: inline;
filename=photo.jpg
Content-Type: image/jpeg;
name=”photo.jpg”
Content-Transfer-Encoding: base64

Notice the Content-Type of “multipart/mixed.”  No real surprise there… just that when you send a message using the Email client launched by an iPhone application, this is almost always “multipart/alternative”.  First “hmmm.”

Secondly, when I used my methodology of using embeded images (<img src=”data:image/png;base64[my data here in base64]”>), this gets cobbled out as

Content-Type: text/html;
charset=utf-8
Content-Transfer-Encoding: quoted-printable

by the Apple Email Client.  By contrast, Apple creates it’s image attachments by doing the following:

Content-Disposition: inline;
filename=photo.jpg
Content-Type: image/jpeg;
name=”photo.jpg”
Content-Transfer-Encoding: base64

So – why not just do the same thing?

Boundaries.

I don’t know what MIME boundaries (the “Apple-Mail-1-782786827” above) Apple is using to segregate its constituent email pieces (text, html, images, attachments) ahead of time – and they are generated dynamically by the email client when putting together it’s messages.

Bummer.

If I knew what the boundary tag was, I could fake out the client, create my own inline image, and boogey on down.

Now, even though this does seem like a serious roadblock, it may not be a total loss, because it presents both

  • (a) a possible direction to look (fake out a boundary, unlikely though it may be) and
  • (b) it presents another solution path (go down to the socket level and code my own SMTP alternative, so that I can create the MIME code directly to send out my attachments).

(a) looks to be a total non-starter, as it looks like the boundary is made – in part – of a timer component, and there is no way to guess how long someone will keep the message open before sending to reliably fake this out each and every time.  (b) will absolutely work – provided you are motivated to write this dude.  Looks like a commercial opportunity for a Cocoa Touch class, and I may yet do this.

A final and as yet unspoken work around would be to know how Apple preps an image in their email client before sending, so that it’s Mail.app knows to wrap the image up as an inline attachment.  Unfortunately, I don’t know of anywhere I can look to see how they compose their messages (no view source – dang!).

All in all, this has stimulated me to investigate some additional paths for a problem that I thought I had a reasonable solution for – and for many, I do and did.

Onward and upward.

Objective-C and HTTP Basic Authentication

Objective-C and HTTP Basic Authentication

For all the really nice stuff Objective-C lets you do on the iPhone, there are many, many holes left for very common tasks.

One of those tasks is Base64 encoding.

Base64 encoding is used to convert binary data into text that can be transmitted using HTTP (like, say, for embedding images in email) or for somewhat obfuscating user ids and passwords to be sent “in the clear” over HTTP (like for Basic Authentication).

Without much further ado, I present one of a gagillion implementations of Base64 encoding in C:

static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"+/";

int encode(unsigned s_len, char *src, unsigned d_len, char *dst)
{
unsigned triad;

for (triad = 0; triad < s_len; triad += 3)
{
unsigned long int sr;
unsigned byte;

for (byte = 0; (byte<3)&&(triad+byte<s_len); ++byte)
{
sr <<= 8;
sr |= (*(src+triad+byte) & 0xff);
}

sr <<= (6-((8*byte)%6))%6; /*shift left to next 6bit alignment*/

if (d_len < 4) return 1; /* error - dest too short */

*(dst+0) = *(dst+1) = *(dst+2) = *(dst+3) = '=';
switch(byte)
{
case 3:
*(dst+3) = base64[sr&0x3f];
sr >>= 6;
case 2:
*(dst+2) = base64[sr&0x3f];
sr >>= 6;
case 1:
*(dst+1) = base64[sr&0x3f];
sr >>= 6;
*(dst+0) = base64[sr&0x3f];
}
dst += 4; d_len -= 4;
}

return 0;

}

And here is how one may transmit a user id and password (using NSURLConnection) to establish a connection using Basic Authentication:

myApp.loginString    = (NSMutableString*)[[NSUserDefaults standardUserDefaults] stringForKey:kloginKey];
myApp.passwordString = (NSMutableString*)[[NSUserDefaults standardUserDefaults] stringForKey:kpasswordKey];

NSMutableString *dataStr = (NSMutableString*)[@"" stringByAppendingFormat:@"%@:%@", myApp.loginString, myApp.passwordString];

NSData *encodeData = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
char encodeArray[512];

memset(encodeArray, '\0', sizeof(encodeArray));

// Base64 Encode username and password
encode([encodeData length], (char *)[encodeData bytes], sizeof(encodeArray), encodeArray);

dataStr = [NSString stringWithCString:encodeArray length:strlen(encodeArray)];
myApp.authenticationString = [@"" stringByAppendingFormat:@"Basic %@", dataStr];

// Create asynchronous request
NSMutableURLRequest * theRequest=(NSMutableURLRequest*)[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.somewebdomain.com"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[theRequest addValue:myApp.authenticationString forHTTPHeaderField:@"Authorization"];

NSURLConnection * theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
else {
[myApp addTextToLog:@"Could not connect to the network" withCaption:@"MyApp"];
}

Hopefully, this will help people get cracking that were having a hard time getting a handle on the fact that Objective-C is really C – albeit with funky class extensions.