How to Detect the Height of a UIWebView

How to Detect the Height of a UIWebView

UIWebView

A nifty solution from Stack Overflow.

Here’s my particular implementation, where I simply measure the height of the document.body element:


    NSString *output = [myWebView 
                        stringByEvaluatingJavaScriptFromString:
                        @"document.body.offsetHeight;"];
    NSLog(@"height: %d", [output intValue]);

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.

The Cobbler’s Children Have No Shoes

The Cobbler’s Children Have No Shoes

You know the old saw.

The Cobbler’s Children Have No Shoes.

Same’s been true with me and updating my own marketing material lately.

To correct that, I’ve been playing around with Animoto this week (http://www.animoto.com).

Verdict: Me like. Me like lots.

Anyway, here’s the result:

iPhone Apps

Android Apps

Blackberry Apps

Underline Text on the iPhone

Underline Text on the iPhone

One of the least talked about feature deficiencies of Apple iPhone SDK Font Handling is the inability to underline text.

You know – like you see in every web link on every web page on the internet.

Yeah.  That.

For me, where I need this the most is when I’m trying to create a link on one of my text sections or display elements, that will invoke a web URI and go off to do some work.

There isn’t a way to style underline fonts included with the iPhone, and Apple doesn’t make it easy at all to include custom fonts without a great deal of pain and gnashing of teeth.

What is one to do?

Well, I don’t know what the rest of you guys do, but this is what I did.

I created a button class (UnderlineButton) that subclasses UIButton and implemented my own drawRect function to draw the underline myself.

This isn’t rocket science, but works quite nicely.  The button should probably have a number of init functions to do things like set underline stroke width and color, but I needed a quick and dirty solution and had about 30 minutes to write, test, and deliver.

The code below is what I wound up with (UnderlineButton.m follows; UnderlineButton.h is simply a stub class that contains only @interface UnderlineButton : UIButton {}).


//
//  UnderlineButton.m
//
//  Created by David Hinson on 11/24/09.
//  Copyright 2009 Sumner Systems Management, Inc.. All rights reserved.
//

#import "UnderlineButton.h"
@implementation UnderlineButton

- (id)initWithFrame:(CGRect)frame {
  if (self = [super initWithFrame:frame]) {
    // Initialization code
    }
  return self;
}
- (void)drawRect:(CGRect)rect {
  [super drawRect:rect];
  CGContextRef context = UIGraphicsGetCurrentContext();

  CGContextSetRGBStrokeColor(context, 62.0/255.0, 62.0/255.0, 62.0/255.0, 1.0);

  // Draw them with a 1.0 stroke width.
  CGContextSetLineWidth(context, 1.0);

  // Draw a single line from left to right
  CGContextMoveToPoint(context, 0, rect.size.height);
  CGContextAddLineToPoint(context, rect.size.width, rect.size.height);
  CGContextStrokePath(context);
}

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

@end

To use the button in an actual application, you would do something like the following:

  CGSize constraintSize, offset1;
  constraintSize.width  = 300.0f;
  constraintSize.height = MAXFLOAT;

  NSString * btnText = @"My Button Text";

  UnderlineButton * myButton = [[UnderlineButton buttonWithType:UIButtonTypeCustom] retain];
  offset1                    = [btnText sizeWithFont:[UIFont systemFontOfSize:16]
                                constrainedToSize:constraintSize
                                lineBreakMode:UILineBreakModeTailTruncation];
  myButton.frame = CGRectMake(20, 164, offset1.width, offset1.height);
  [myButton setTitle:btnText forState:UIControlStateNormal];

  [myButton setTitleColor:[UIColor darkTextColor] forState:UIControlStateNormal];
  [myButton setFont:[UIFont systemFontOfSize:16]];
  [myButton addTarget:self action:@selector(doButtonTouch:)
            forControlEvents:UIControlEventTouchUpInside];

  [cell myButton];
  [myButton release];

The doButtonTouch method will perform whatever it is you want to do; in my case this invoking another method to slide in a UIWebView to show drill down content.

Summary: Underline Text. Nothing there to make it natively easy, but workable solutions can be cobbled together.

But I will be the very first to say “it shouldn’t be this hard.

You’d think with all my video game experience that I’d be more prepared for this

You’d think with all my video game experience that I’d be more prepared for this

Well.

I finally got “bit” by the current Apple mania for ferreting out private API calls in App Store iPhone Applications.

You’d think with all my video game experience that I’d be more prepared for this (props to Toy Story 2).

Anywho, these are the two lines of code that got my submission smacked out of the park:

[[UIApplication sharedApplication] terminate];

and

[NSHost currentHost];

Doesn’t look like much, does it?

Still, verboten all the same.

In the case of the terminate function call, all I’m trying to do gracefully end the application in the event that network connectivity is not available.  There is a simple workaround here, and that is to instead use the C Language exit(0) call.  Easy-peazy, lemon-sqeazy.

The second “offending” call, [NSHost currentHost], is simply a call to get the iPhone’s current IP address.  The workaround here is to do something else, like this.

In any event, both of these changes were minor.

But frustrating.

Was I in the “wrong” for using them?  In the view of Apple, absolutely.  And in view of what I agreed to as an iPhone Developer publishing apps on the App Store, again, absolutely.

But, believe it or not, I don’t memorize every API call and know right off the top of my head whether it’s official or not.  Shocking, I know.

And, since the app that was being submitted has been in the App Store for over a year (as have these forbidden calls), it wasn’t like I was trying to sneak in some neato feature available only to Apple by using private calls.

Long story longer, I can easily correct these transgressions – and have done so – in about 15 minutes time.

The bad news is that the app sat in the approval queue for ten (10) days just to be rejected.  An app that has passed numerous times before.  And now must be resubmitted and waited upon.  Again.

Another seven-to-fourteen visit to the purgatory of App Store Approval.

If you think you’re gonna sneak some hidden feature in, reconsider.

Unless, of course, you have all the time in the world to resubmit offending apps.

An Open Note to Mobile Ad Networks – Show Me The Money

An Open Note to Mobile Ad Networks – Show Me The Money

It seems like once every couple of weeks I’m complaining about the see-saw nature of mobile advertising rates.  Of late, the rates have been what I consider to be abysmal (sub $4 to low $3 CPM).

This is particularly frustrating if you’re serving up between 500,000 and 1,000,000 impressions per month.

And seemingly, just as often, I’m approached by competing ad networks wanting me to switch to their platform.

They always want to talk about how they can pick and choose between several publishers and that their tech is superior.

You know what I want to know?

How much am I going to get paid.  Period.

Seems harsh, but here’s what I’m supplying and risking for switching to any ad publisher: my (or more properly, my app’s) reputation and traffic.

If they are only offering what I’m getting today – or less – what’s the point of even talking?

Today the cycle repeats.  I have an ad network calling to make their pitch, and I can almost predict the spiel.

Mr. Ad Network cubicle guy, you REALLY want my business?

Pay me a signing bonus for switching to your network.  Pay me for the time I have to take to integrate your ad client into my software, unit and system test it to make sure it doesn’t blow up under load, and bake it for 2-3 weeks in the App Store approval queue.

But please, don’t come to me expecting me to switch because you have the best brokering system since bartering began.

Show Me the Money.