Programmatically Removing a Tab from UITabBarController

Programmatically Removing a Tab from UITabBarController

There is a right way, and a wrong way, to remove a tab from a tab bar in a tab bar controller within an iOS app. I’m not going to show the wrong way.

Without fanfare:

        // Remove the third tab from a tab bar controlled by a tab bar controller
	NSMutableArray * vcs = [NSMutableArray 
                                arrayWithArray:[self.tabBarController viewControllers]];
	[vcs removeObjectAtIndex:2];
	[self.tabBarController setViewControllers:vcs];
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.

Sorting NSMutableArrays

Sorting NSMutableArrays

For whatever reason, sorting arrays in Objective-C perplexes a great many budding iOS developers.

However, it can be entirely straightforward.

The example below takes a mutable array (in this case, a set of businesses) and sorts them either by a point value or by a distance value, depending upon the state of a button from the UI.

The heavy lifting is all done by the comparison functions compareProximity and comparePoints.

The code is fairly self explanatory. I’ve written the comparison functions in a way as to be totally explicit about what values need to be returned.

Pay particular attention; the comparison function comparePoints is written to sort the array in descending point order; compareProximity sorts the array in ascending distance order.

// Compare Result for Sorting by Proximity
NSComparisonResult compareProximity(Business *b1, Business * b2, void * context) {
	if (b1.distance<b2.distance) {return NSOrderedAscending;}
	if (b1.distance>b2.distance) {return NSOrderedDescending;}
	if (b1.distance==b2.distance) {return NSOrderedSame;}

	return NSOrderedSame;
}

// Compare Result for Sorting by Points, Descending
NSComparisonResult comparePoints(Business *b1, Business * b2, void * context) {
	if (b1.points<b2.points) {return NSOrderedDescending;}
	if (b1.points>b2.points) {return NSOrderedAscending;}
	if (b1.points==b2.points) {return NSOrderedSame;}

	return NSOrderedSame;
}

// React to a button push from the screen
-(IBAction)doSort:(id)id {
	UIButton* btn = (UIButton*)id;

        // Toggle button on display
	if (btn.tag==0) {
		[btn setImage:[UIImage imageNamed:@"points_image.png"] 
                        forState:UIControlStateNormal];
		btn.tag=1;
		}
	else {
		[btn setImage:[UIImage imageNamed:@"proximity_image.png"]
                        forState:UIControlStateNormal];
		btn.tag=0;
		}

        // Order Elements (Perform the Sort)
	[self orderElements];

        // Refresh the data in UITableView
	[self.table reloadData];
}

// Perform the sort, depending upon button state
-(void)orderElements {
	if (self.sortButton.tag==1) {
		[self.businesses sortUsingFunction:compareProximity context:NULL];
	        }
	else {
		[self.businesses sortUsingFunction:comparePoints context:NULL];
	        }
}
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.

In Flanders Fields

In Flanders Fields

In Flanders fields the poppies blow
Between the crosses, row on row,
That mark our place; and in the sky
The larks, still bravely singing, fly
Scarce heard amid the guns below.

We are the Dead. Short days ago
We lived, felt dawn, saw sunset glow,
Loved and were loved, and now we lie,
In Flanders fields.

Take up our quarrel with the foe:
To you from failing hands we throw
The torch; be yours to hold it high.
If ye break faith with us who die
We shall not sleep, though poppies grow
In Flanders fields.

– John McCrae

You Never Know Who’s Watching

You Never Know Who’s Watching

I received a very kind message this morning from a classmate from high school. We weren’t particularly close, but shared many classes together over a span of four, five, maybe even six years.

He said some very kind things about the influence I had. It made me feel tremendous, and humbled that I could have that sort of positive impact that someone would remark upon it so far down the road.

But it also reminded me – again – that even when you think no one is looking, someone is always watching how you carry yourself.

I thought of a story I heard from Sarah Cannon – “Minnie Pearl” from the Grand Ole Opry in Nashville – while an employee at Opryland many years ago. She spoke of a rainy afternoon matinee she and her troupe were performing in front of five or six people in the audience. The performance was bad, the talent didn’t really give their all, and she remembered simply “phoning in” the effort. One of the members of the small audience was a scout for the Broadway show “Oklahoma.”  Needless to say, she didn’t land a role. She wondered for years how things might have gone had she just remembered – someone is always watching.

Before school this morning, I shared the message from my friend with my ten year old, to let him know that people take notice of the way we treat people, how hard we work, the deeds – good or bad – that we do. Even when we think no one is looking.

Life isn’t always fair.

But that doesn’t mean we can’t do some small amount of lasting good just by being the best people we can be; for ourselves, and for those observing how we choose to live.