Underline Text on the iPhone
November 26, 2009
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.“
Does the Internet Devalue Everything It Touches?
November 25, 2009
My wife and I spoke at length yesterday about how seemingly the Internet devalues everything it touches.
Zero friction. Free news. Ninety-nine cent songs and applications.
Instant gratification at zero cost.
My business continually responds to customers wanting “steak” application features, but coming to the table with “baloney” budgets.
This has really always been the case, but the signal-to-noise between serious-minded buyers and buyers wanting everything-for-nothing is beginning to get deafening.
Regardless of what our perceptions are being trained up to be, delivering quality content of any stripe – performance, applications, knowledge, entertainment – was developed, incubated, curated, and executed – at a cost.
In most cases, significant cost.
And yet the perception in the marketplace of the interwebs is that there is always a free solution to any problem. Or at least a solution that one can pay someone next to nothing to solve.
Expecting premium service, zero defects, and 100% uptime.
There will come a point when we collectively look around and wonder where all the professionals went.
Why all we see and consume is substandard and shoddy.
Why the only stores that remain open in our towns are Walmarts, Home Depots, and Best Buys.
Don’t look now, but you’re soaking in it.
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.
iPhone Template
November 6, 2009
Park Mapping Application – Augmented Reality “Walkabout”
November 5, 2009
Conflict Resolution
November 1, 2009
Ah, the running theme in my life this week – professionally and personally.
Conflict Resolution.
Many people mistake this for Conflict Avoidance. Or Conflict Deterrence.
Make no mistake – conflicts are best handled by recognizing them when they occur, determining what the best possible choices are for resolving the conflict (hopefully, amicably), and taking action to see the resolution fulfilled.
How did I do this week? OK in some cases. Great in others. Less than what I had hoped for in still others.
Even so, I consider all conflict resolutions to be positive, even when the outcomes are less than optimal for me personally.
Because it lessens by one the number of issues I must face moving forward, and I know that once I have addressed and handled a conflict it frees that energy up for the next thing coming down the pike.
I learned a lot this week through the ebb and flow of conflicts. Much that I will use in my future dealings and interactions as a business manager, and as a person.
Though I do wish the ride along the waves in resolving life’s clashes of opinion was a little less tumultuous at times.
Looking forward to calmer waters – and mostly positive outcomes – this week.
Cheap Gas! Version 3.0 Now in iTunes! Augmented Reality is HERE!
September 24, 2009
Exact Imitation is the Most Sincere Form of Thievery
September 19, 2009
OK, Apple.
I turned the other cheek when another app with the name “Cheap Gas” showed up in the app store. That was also a GasBuddy.com client, and differed in name only by some hyphenated bullshit tacked onto the end of “Cheap Gas.”
Fine. It’s a crowded little space (GasBuddy.com, GasBag, iGasUp, Cheap Gas!). Build a better mousetrap, yada yada yada. Fine.
When a THIRD app shows up – named “Cheap Gas Finder” – and uses the “Cheap Gas!” application in iTunes VERBATIM as their app description… well, let’s just say “enough’s enough.”
It’s not like Cheap Gas! is an obscure app. It’s not a world beater, but it’s fairly well known, with well over 400,000 downloads.
How in the hell can anyone simply tack on a word of a popular app, steal their description VERBATIM, and still get approved into the App Store?
Am I pissed? Really, more disappointed than pissed.
I’m used to being kissed before I get screwed.
So, there’s THAT.
Below are the screen shots of my app, and the two copycat apps.
Cheap Gas! Version 3 Submitted to iTunes… New Augmented Reality Feature
September 14, 2009
Sometimes Intellectual Curiosity is a Commercial Waste of Time
August 24, 2009
I had an exchange this morning with one of my twitter buds and fellow Orlando tech person, @kurtisnelson.
I had been doing a little digging into what it would take to add printing to one of my iPhone apps, Cheap Gas!. Namely, I wanted to have the ability to look up the cheapest stations nearby from my iPhone, and then print out a copy of the listing.
As a means of seeing what the interest level would be for such a feature, I asked my twitter stream what they thought of the idea. Kurt was one of the first to respond with the following:
“I don’t see any point in it, but go for it if your purpose is to learn how to do it.”
First let me say, the feedback was very valuable for gauging interest. Kurtis wasn’t interested. If everyone feels that way, then obviously it makes little sense to devote time, toil, sweat, and tears to adding that particular feature.
The second part of his response, where he said “go for it if your purpose is to learn how to do it”, brings up an interesting question. When should intellectual curiosity lead development of new features, and when should it be shelved to working on things that actually add new value?
In fairness, I’ve biased my response in the title of this post.
There are times when you take a risk, and do spec development, because you believe in an idea or dream. Maybe you think you have an angle on a feature that user’s will love – and they just don’t know it yet.
And sometimes, your users are just straight up right. Don’t fix something that ain’t broke.
About twenty years ago, I had a friend and fellow co-worker open a CD store in Nashville. It was in a hip, high traffic area.
And he only sold music that he, personally, liked.
The store failed. Twice (he moved the store to an even more high traffic location with an even hipper potential clientele).
Sometimes our personal likes and intellectual curiosities lead us down paths that sap limited resources (time, money, talent – you name it).
Am I saying not to be intellectually curious? Not at all.
I’m saying that, when you have a commercial venture and you have limited time, money, and talent to create goods and services, idle intellectual curiosity is a huge – and sometimes, lethal – distraction to supplying your customers with features that they want, will use, and will crow to the heavens about.


