iPhone: UITextField and the virtual keyboard, part II
April 20, 2009 8:42 pmI’ve written about this subject before, but I didn’t do a very good job. Many weeks of intensive iPhone programming later, I am ready to deliver my definitive treatise on this subject.
The Problem
Say you’ve got a UITextField that you want the user to type something into. It’s located near the bottom of the screen. The user touches the control to begin editing. The virtual keyboard pops up, which completely covers the UITextField. It is now impossible for the user to see what she’s typing. You have to write your own custom code to move the text field out of the way before the keyboard pops up, so the user can see it.
This is against the spirit of Cocoa. AppKit on the Mac and UIKit on the iPhone normally give you decent behavior by default, and lots of hooks you can use for customization, if the defaults don’t suit your situation. In this case, they gave us a big problem by default and very little guidance towards solving it. Apple’s own example code does not do a good job with this. What we’re left with is lots of blog authors like me writing half-baked solutions to partial subsets of the problem, whereas Apple could have solved all of it, easily. Disappointing.

keyboardscroll screen-shot
Having lived with this for awhile, I thought about how best to write something definitive, so I don’t have to keep tripping over this issue again and again. The solutions I’ve seen on programming forums and blogs involve code snippets you can drop into an existing view controller, but you have to do so every time you write a new view that contains text fields. The height of cut-and-paste code reuse.
My first idea was to add a category to UIViewController. Then every new view controller I write from now on would get textfield-handling ability “for free.” But Apple has already solved this problem for the UITableView object with special code inside UITableViewController, as of iPhone OS version 2.2. It’s not right to apply my fix to every view controller if some don’t need it. So instead I implemented a descendant of UIViewController, which can be derived from to create new view controllers.
The Solution
My keyboardscroll Xcode project shows you two ways you can solve this problem. If you’re using a UITableView, the issue is solved for you by UITableViewController, if you get all the connections right. I’ve included a table view controller that works this way, because it was harder than I thought it would be. My first attempt failed miserably. If you’re using some other view type, you can write a new view controller descended from my own WBKeyboardViewController class, which the example project shows you how to do.
My view controller works for views that contain any number of UITextFields. The content view can be a UIView or UIScrollView, either works fine. Both portrait and landscape modes are fully supported.
The only serious limitation is that my code doesn’t cope with UITextViews, which the user can also type into, and are subject to the same problem. I don’t have enough experience with text views to have a good idea about how to approach that problem yet. My first guess is that my view controller could be made to deal with them fairly easily, with a few special cases here and there.
To the greatest extent possible, I’ve avoided hard-coding pixel counts. For example, the portrait mode virtual keyboard is 216 pixels tall, and the landscape one is 162 — but those two numeric constants do not appear anywhere in the code. The proper values to use are retrieved from the operating system. Apple’s own UICatalog sample project hard-codes the height of the keyboard, so I didn’t discover that there is a better way until I had been searching for articles about this for some time.
Hard-coded pixel counts are not just a theoretical concern. “In iPhone OS 1.1.4 and earlier, the keyboard height in landscape orientation was 180 pixels.” Straight from Apple’s own documentation. Of course, that was from before the iPhone SDK existed. I think they’d be reluctant to make such a change today, since it would break so many third-party apps. Better safe than sorry, though.
There are 40 total UITextFields in my example project spread over two views, and they always, always get out of the way before the keyboard appears. I’ve tested this code thoroughly in the simulator. I’ll be using it on a real iPhone soon, so if there are issues in that scenario, I’ll find them and update this blog post. There are no bugs that I’m aware of. If you find one, I’d love to hear about it, so I can fix it.
Landscape Mode is the Devil’s Own Handiwork
Whew, this was way more complicated than I thought it would be. Apple chose to implement the landscape modes far differently than I would have. I say “modes” rather than “mode,” because there are two of them. The iPhone’s user interface can be rotated to one of four orientations: portrait, landscape, portrait upside down, and landscape upside down.
When your view rotates into landscape mode, almost all of UIKit remains stuck in portrait mode. That means you have to write lots of special cases. Here’s the first example. This code snippet gets you the bounds rect of the iPhone’s screen:
CGRect rect = [[UIScreen mainScreen] bounds];
In portrait mode, the rect returned has a height of 480 pixels and a width of 320, which is correct. In landscape mode, the rect returned is exactly the same as the one you get in portrait mode. Erm. Okay, it’s easy enough to write a wrapper function that swaps the rect’s width and height when appropriate, which I did.
Next problem. UIView contains methods for converting rects and points from the coordinate system of one view into another, convertRect:toView: being one of them. I’ve successfully used those methods in portrait modes, but not in landscape modes. It appears to me that, while your view is in landscape mode, the main window remains in portrait mode, so translations between the two are nonsensical. I couldn’t make it work, anyway. Could be because they’re weird in the way I just described, or it could be because I don’t know what I’m doing. Either way, I had to abandon UIView’s conversion methods and roll my own.
The next problem I ran into indicates the UIKit’s designers’ intent quite strongly. UIApplication has a property called statusBarFrame. Normally I wouldn’t need to know the size of the status bar, but I was forced to roll my own coordinate conversion methods, remember. In portrait mode, the property returns a width of 320 pixels and a height of 20 pixels, which is correct. In landscape mode, it says the status bar is 20 pixels wide and 480 pixels tall! That’s obviously wrong, from the perspective of your landscape-mode view. It would be correct only from the perspective of a parent view that’s still in portrait mode. Yet another special case I had to write for the landscape modes.
It took me three days to get my view controller working right, despite the fact that it is only around 250 lines of code. Largely because I have come to trust that UIKit will Do The Right Thing in almost every case, so I could barely conceive of the idea that it would ever tell me that the status bar is 480 pixels tall with a straight face. Not at all what I was expecting, so I had to abandon a lot of early code and rethink my assumptions.
Here’s the code: keyboardscroll.zip
Categories: cocoa, iphone, objective-c, programming
Comments Off
details
April 9, 2009 8:15 amAs of this writing, a google search for “UIImage rotate” turns up my previous blog article as the third hit. If you ask me, my take on the subject is far more useful than the top two hits. Yes, I am a shameless braggart, thanks for noticing.
The reason I wanted to rotate UIImages is because I’m working on an iPhone card game, soon to be revealed. While casting about the ‘net for playing card images to use, I came across this excellent page at www.jfitz.com. There you will find the original Windows-3-era playing card images, made famous by Windows Solitaire, everyone’s favorite time-waster. I can recall whiling away countless hours with that game myself, back when I was a Windows user. One day in the mid nineties I was poking around in my Windows directory and came across CARDS.DLL, which I opened up with the resource editor that came with Microsoft’s compiler of that era, and discovered the bitmaps for all those playing cards lurking inside. I spent ten minutes contemplating writing a Windows card game, then promptly forgot about it. I didn’t know until recently that those iconic card images were created by the legendary Susan Kare, the woman who earlier made the refreshing, tiny black-and-white icons for the original Macintosh. Check out the one she made depicting Steve Jobs. It captures the essence of him in a mere 32 bytes.
Susan Kare’s Windows playing cards turned out to be perfect for my game. Their dimensions are close to ideal, given the size of the iPhone’s screen. If they were any bigger, you wouldn’t be able to fit enough of them onscreen at once. Any smaller and the player would have trouble selecting individual cards with her fingers.
Ugly green pixel
I used these graphics for a couple of days and then noticed a problem. The card images all have an unnecessary green pixel in each corner. I originally planned to work around it by using that same shade of green for my background, so that the unwanted pixels would be “invisible.” That works okay, sort of, until you draw cards on top of other cards. I refer you now to the picture I’ve included with this post. Look at that! Ugly green pixel in the midst of an otherwise pristine sea of retro Windows-3-era opulence!
The wise move would be to ignore it. I have more work to do than I can finish in this lifetime. It’s a minor detail that can wait until after I find out if anybody will ever play this thing. Sadly, this is me we’re talking about. One thing I don’t like about myself is my inability to ignore minor junk like this and focus on the bigger picture. But there it was in my head as I was working on this, over and over: ugly green pixels, ugly green pixels.
The proper fix is that the pixels that are now green need to be made transparent. I recently bought a license for Acorn, specifically to do programming-related graphics work such as this. Acorn may be capable of making a single pixel transparent, but if so, I can’t figure out how.
I evaluated other options. Make the green pixels transparent at runtime, just before displaying the images? A quick scan of the docs told me that UIImage doesn’t make it very easy. Try a different image editor? I downloaded a couple others, which were downright impenetrable, compared to Acorn. I had a look at the docs for libpng, thinking I might write a one-off utility to transparent-ize the pixels. libpng is a great piece of software, but I quickly determined that it would take me days to figure out the API well enough to do what I want.
You’re bored so let’s cut to the chase: the solution was ImageMagick. I was turned off initially, because it has that faint musty unix smell that makes me fear I’m in for trouble. I wasn’t wrong! I tried to install it via MacPorts, which gave me a fresh new version of the usual rambling useless error messages it always gives me. Maybe it thought my X11 version was too old? As if I really want to spend all day figuring out if that is in fact what it’s upset about, or how I would install a later version of X11 that I’m never going to use. So I downloaded a prebuilt binary distribution from the ImageMagick site, which was, predictably, a pain in the ass to set up. Unix motto: making simple things tedious and unpleasant for four decades!
Have to give them props, though: ImageMagick is a powerful tool. Thirty minutes of poking and prodding later, I devised this bash script to convert my entire folder-full of images at once:
#!/bin/sh for file in $(ls *.png); do echo "converting $file ..." convert $file -transparent "rgb(0,128,0)" $file done
Problem solved. Drawing the new-and-improved images in the iPhone simulator does exactly the right thing. The formerly-green pixels are now invisible. I tried drawing a UIImage directly and also putting it inside an UIImageView, both methods work perfectly.
The practical upshot is that my stupid brain can finally stop fixating on this minor detail, and hopefully I can get back to work.
Categories: bash, iphone, programming
Comments Off
iPhone: UIImage rotation and mirroring
March 30, 2009 2:19 amUpdate: This post has been superseded. Please go read this instead: UIImage rotation and scaling.
My access logs show that you webbernauts like my iPhone articles better than anything else on this site. Very well then, here’s another one.
I recently found myself with a need to rotate the contents of a UIImage 90 degrees to the right or left and then display it. Apparently this is easy if your image is inside a UIImageView and you’re willing to rotate the whole view, but that’s not what I have in mind. Rotating the UIImage itself turns out to be pretty danged complicated, and requires a great deal of math. I suck at math.
Fortunately for me, the code given in this guy’s blog post tackles 95 percent of the problem, and 100 percent of the math. Heh! It’s not quite what I want, however. The function given in that blog post queries the EXIF data inside the image and rotates it based on that. I want the method’s caller to be able to specify what type of rotation should be done. I tinkered with the code for awhile and got exactly what I wanted.
The original blog post’s code was written as a standalone C function. I wrote my version as a category attached to UIImage. Say you’ve got a UIImage you want to rotate 90 degrees left. It’s done like this:
newImage = [oldImage rotate:UIImageOrientationLeft];
newImage will be a newly-created copy of oldImage, rotated 90 degrees left. Other options include UIImageOrientationRight, UIImageOrientationDown (for a new image that’s an upside-down copy of the original), and so on. There are also “mirrored” variants, which both rotate the image and mirror its contents left-to-right.
In the process of getting it to work the way I wanted, I made a bunch of changes to the code. I reformatted it so that the lines are no longer than 78 characters. (One of these days I’m going to write a blog post about why I think that’s a good thing.) The original function had two or more unnecessary copies of some data and superfluous calls to external functions, which I eliminated. The old code trimmed the size of the original image if it was bigger than a certain size, which I eliminated. The original had several copies of an identical clause for swapping the width and height of a CGRect, which I refactored into a separate helper function. There are other changes as well, but I’ll spare you the details.
Okay, one more change that I’m going to document. I am 85 percent sure that the original code has a bug. It does not work properly when the input orientation is UIImageOrientationLeftMirrored or UIImageOrientationRightMirrored. The new image gets partially chopped off along one edge, which I fixed. It could be that it was not a bug in the context where the original code was being used, but it definitely is in this new context. I’ve tested all orientations in the iPhone simulator, and they all work.
First, here’s UKImage.h, which defines the category.
// UKImage.h -- extra UIImage methods // by allen brunson march 29 2009 #ifndef UKIMAGE_H #define UKIMAGE_H #import <UIKit/UIKit.h> @interface UIImage (UKImage) -(UIImage*)rotate:(UIImageOrientation)orient; @end #endif // UKIMAGE_H
Now here’s UKImage.mm, which defines the rotate: method. You can rename this file to UKImage.m if you never use any C++ constructs in your code.
// UKImage.mm -- extra UIImage methods // by allen brunson march 29 2009 // based on original code by Kevin Lohman: // http://blog.logichigh.com/2008/06/05/uiimage-fix/ #include "UKImage.h" static CGRect swapWidthAndHeight(CGRect rect) { CGFloat swap = rect.size.width; rect.size.width = rect.size.height; rect.size.height = swap; return rect; } @implementation UIImage (UKImage) -(UIImage*)rotate:(UIImageOrientation)orient { CGRect bnds = CGRectZero; UIImage* copy = nil; CGContextRef ctxt = nil; CGImageRef imag = self.CGImage; CGRect rect = CGRectZero; CGAffineTransform tran = CGAffineTransformIdentity; rect.size.width = CGImageGetWidth(imag); rect.size.height = CGImageGetHeight(imag); bnds = rect; switch (orient) { case UIImageOrientationUp: // would get you an exact copy of the original assert(false); return nil; case UIImageOrientationUpMirrored: tran = CGAffineTransformMakeTranslation(rect.size.width, 0.0); tran = CGAffineTransformScale(tran, -1.0, 1.0); break; case UIImageOrientationDown: tran = CGAffineTransformMakeTranslation(rect.size.width, rect.size.height); tran = CGAffineTransformRotate(tran, M_PI); break; case UIImageOrientationDownMirrored: tran = CGAffineTransformMakeTranslation(0.0, rect.size.height); tran = CGAffineTransformScale(tran, 1.0, -1.0); break; case UIImageOrientationLeft: bnds = swapWidthAndHeight(bnds); tran = CGAffineTransformMakeTranslation(0.0, rect.size.width); tran = CGAffineTransformRotate(tran, 3.0 * M_PI / 2.0); break; case UIImageOrientationLeftMirrored: bnds = swapWidthAndHeight(bnds); tran = CGAffineTransformMakeTranslation(rect.size.height, rect.size.width); tran = CGAffineTransformScale(tran, -1.0, 1.0); tran = CGAffineTransformRotate(tran, 3.0 * M_PI / 2.0); break; case UIImageOrientationRight: bnds = swapWidthAndHeight(bnds); tran = CGAffineTransformMakeTranslation(rect.size.height, 0.0); tran = CGAffineTransformRotate(tran, M_PI / 2.0); break; case UIImageOrientationRightMirrored: bnds = swapWidthAndHeight(bnds); tran = CGAffineTransformMakeScale(-1.0, 1.0); tran = CGAffineTransformRotate(tran, M_PI / 2.0); break; default: // orientation value supplied is invalid assert(false); return nil; } UIGraphicsBeginImageContext(bnds.size); ctxt = UIGraphicsGetCurrentContext(); switch (orient) { case UIImageOrientationLeft: case UIImageOrientationLeftMirrored: case UIImageOrientationRight: case UIImageOrientationRightMirrored: CGContextScaleCTM(ctxt, -1.0, 1.0); CGContextTranslateCTM(ctxt, -rect.size.height, 0.0); break; default: CGContextScaleCTM(ctxt, 1.0, -1.0); CGContextTranslateCTM(ctxt, 0.0, -rect.size.height); break; } CGContextConcatCTM(ctxt, tran); CGContextDrawImage(UIGraphicsGetCurrentContext(), rect, imag); copy = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return copy; } @end
Categories: cocoa, iphone, objective-c, programming
Comments Off
newspocalypse y2k9
March 22, 2009 6:53 amA friend sent me a link to a Red Meat comic recently. I used to read that strip religiously, but I abandoned it about a decade ago. Which is how I came to read Max Cannon’s desperate screed about the shrinking of “local alt weeklies,” as he calls them.
I feel for the guy, as well as the many other cartoonists who he says are in the same boat. I do! Their world is being rent asunder beneath their feet. Things will probably get worse before they get better. But I tell you the one damn thing that absolutely will not work: having your readers wage a letter-writing campaign to the editors. These institutions are fighting for their very existence. You and your devoted readers are barely a blip on their panicked radar.
It’s not just local alt weeklies with a problem. Earlier this month, the Rocky Mountain News went out of business. It was 150 years old. Currently the largest newspaper forced out of business by the ongoing economic recession, but it probably won’t hold that record for long. If the Rocky Mountain News had carried Red Meat, do you think they would have given a rat’s whisker about Max Cannon’s predicament? Ha ha ha.
I used to be pretty worried about this. Blogs are great and all, but which of them is going to have the money to send reporters to Iraq to cover what’s going on over there? The newspapers seem to be trying this tactic as well: “You will miss us when we’re gone!”
Despite feeling bad for all of you in that predicament: actually, no. I won’t miss you. In fact, I wish you’d hurry up and fail more quickly. The sooner you are gone and out of the way, the sooner alternatives can sprout into your former space.
Recently I read two really good articles that changed my thinking on this topic. First: Newspapers and Thinking the Unthinkable, an almost unbelievably good article by the always-insightful Clay Shirky. If you had to boil it down to one short concept, it would be this: “Society doesn’t need newspapers. What we need is journalism.” And then he explains how we’re going to get there. Second: Old Growth Media and the Future of News, by Steven Berlin Johnson. I’d never heard of this guy before, but this article was good enough for me to put him into my RSS reader.
Max Cannon’s screed explains that he doesn’t put strips on his website to make money. He’s just doing it out of the goodness of his heart for his fans. The newspapers are his bread and butter. In that case, Max, it’s time to change your business model. Married to the Sea is a screamingly funny webcomic created by a married couple named Drew and Natalie. They support themselves and their infant child with income from merchandise they sell on the strength of their multiple humor websites.
Despite me not liking his strip that much anymore, I’m sure Max Cannon has his fans. He’s been drawing it for over 20 years now. One would hope that he’s at least as resourceful as all these young whippersnappers who are finding a way to make money off this newfangled “internet” thing? Surely.
Categories: opinion
Comments Off
iPhone: using UITextField with the virtual keyboard
March 5, 2009 12:27 pm
keyboardscroll main window
UPDATE: this blog post is now obsolete. I wrote it before I knew enough about the subject to be pontificating on it. Please read my newer blog post on this subject instead.
Now I feel bad. In the last two days, about ten people found their way to my blog by using search terms that suggest they are having as much trouble with the iPhone keyboard as I did. I spent a whole day figuring it out. Then I wrote a blog entry where I just bitched about it, rather than presenting an actual solution. Sorry for wasting your time, folks. Today I give you an example Xcode project that demonstrates how to code for the iPhone’s virtual keyboard, as an act of atonement.
I got most of this stuff from UICatalog, Apple’s sample project that demonstrates the use of most UIKit controls. Some of it didn’t work very well for me, so I modified it a bit. For example, UICatalog not only scrolls the view up to make room for the keyboard, it also shortens the view at the top by the appropriate number of pixels. That caused unpleasant side effects for me, so I did away with that part.
UICatalog was difficult for me to follow, because they’re trying to demonstrate so much stuff at once. It took me awhile to figure out what parts were relevant to my situation. My keyboardscroll project is quite small, so it should be easier for you.
As mentioned previously, I don’t like Interface Builder. Therefore, this project is “nibless,” completely free of XIBs and NIBs. That shiz be stanky, yo. So you could also consider this project to be an example of how to lay out your views entirely in code, if that’s something that interests you.
The keyboardscroll app presents an interface that looks similar to the iPhone’s SMS app. The top part of the window is taken up by a UITextView full of scrolling text. There is a UITextField at the bottom of the window, where the user can type in new text. When the user presses the “Send” button, the text typed is added to the UITextView at the top.
It is impossible to do iPhone programming without making use of magic pixel counts. To keep the damage to a minimum, I put all the magic numbers at the top of ViewController.m and copiously documented them. Most mysterious of all is how many pixels to scroll up when the keyboard appears. Every time I do this, I need to use a different pixel count. It seems like Apple could give us some help here. Perhaps a method to call that would tell you how far up your view has to go to avoid the keyboard.
Categories: iphone, objective-c, programming
Comments Off
iphone newbie: view coords, UITextField
March 3, 2009 7:49 am
I count three people in my access logs who got here by searching for iPhone programming material, two who are trying to create nibless apps. Rock on, fellow Interface Builder haterz!
iPhone programming is typically very similar to Mac programming. One big change I’ve noticed is that they’ve moved the origin for view coordinates. For NSView on the Mac, the origin point is at the lower left corner, Y coords get bigger as you move up the window. That always seemed deeply, profoundly wrong to me. I guess I’m not the only one who feels that way, because on the iPhone, the origin point is now in the upper left corner, with Y coords getting bigger as you move down the view.
To make matters worse on the Mac, it’s possible to have an NSView with “flipped” coords. In that mode, the origin point is at the top left, as god intended. Some of the stock views are like this, I think NSTableView is one of them. Those views will go crazy if you try to set them back to the “normal” Mac way, displaying their contents incorrectly. What a mess. Looks like iPhone/UIView doesn’t have the “flipped” concept, so everything’s cool again.

Well, except for other annoyances. Today’s culprit: UITextField. This is the standard one-line text input control for the user to type into, analogous to the Mac’s NSTextField. Way, WAY more complicated than it should be. Every single thing that happens to it in its lifetime, you have to write custom code for.
Creating a UITextField and adding it to a parent view is a trial-and-error affair, sprinkled with magic pixel counts like 30.0 and 4.0 and so on. It’s not just me, that’s the way they do things in Apple’s UICatalog sample app, which shows you how to create most control types. This is bad. What if the dimensions of the control change in the next iPhone software update?
When the user touches the control, the onscreen keyboard pops up. Surprise! It will almost certainly cover the UITextField the user is typing into, so they can’t see what they’re doing. The next step is that you have to write code to scroll your view up to get out of the keyboard’s way when it appears, then scroll the view back down when the keyboard disappears. Why isn’t the iPhone OS doing this for me? Madness!
Now you must add code to your view controller’s viewWillAppear: method, to catch notifications for when the keyboard appears and disappears. Then add more code to viewWillDisappear: to disable those notifications. Then write a method that gets called when the keyboard appears or disappears, to scroll your view up or down by a magic number of pixels. UICatalog hard-codes the value 150.0, which is I suppose the height of the onscreen keyboard. That wasn’t quite right for me, I discovered I needed it to be 166.0. I’m sure that will be the right value to use always and forevermore, right Apple? Sheesh.
Finally, the keyboard won’t ever go away unless you write more code to dismiss it. You must make your view controller the text control’s delegate and write a textFieldShouldReturn: method so you can do something with the Enter key and tell the keyboard to go away. Six new methods later, your quest is at an end. Until the next time you need a UITextField.
Categories: cocoa, iphone, mac, objective-c, programming
Comments Off
nibless
February 23, 2009 7:59 pmUpdate: The linked blog post in this article that explains how to create a nibless Xcode project is now dead. Go read this post instead.
This might sound heretical, but here goes: I don’t like Interface Builder.
I used the 2.x versions to build this. I got along with it acceptably well at the time, I guess, although it was never my favorite program. Then Apple completely rewrote it from the ground up for 3.0, whenceforth it didst suck, yea verily.
My complaint is a simple one: Interface Builder is 87 billion times more complex than it should be. Dozens of windows, hundreds of tabs, thousands of buttons, sliders, text input fields, check boxes, outlets, hinges, beads, knobs, joints, pipes, and zippers. Every time I start it up, I dread the ordeal I know I’m in for. It takes me half an hour to find the one check box I need in a sea of extraneous crud. Once I get a view or window set up appropriately, the steps I went through to get there are not easily documented or remembered.
I know that laying out user interfaces and wiring up controls is a big job. I know that somebody must need all that crud I never use, or it wouldn’t be there. But couldn’t Apple use its legendary user interface skills to de-emphasize the infrequently-used parts? I’m dyin’ here.
If you’re writing a Mac/Cocoa app, I guess you’re stuck with it. You’ve probably got at least a few fairly complicated windows to manage, along with dozens of menu items. But if you’re writing an iPhone/UIKit app, the value proposition changes. iPhone apps tend to have only a handful of user interface objects and connections. In this scenario, Interface Builder’s weaknesses outweigh its strengths.
It’s easy to assume that it’s impossible to build an iPhone or Mac app without Interface Builder. As a matter of fact, I once spent several months figuring out how to build Cocoa apps without NIBs, because I was writing a cross-platform GUI framework, so obviously they had to go. It wasn’t easy, because it’s not done very often, but I succeeded. And the first version of the iPhone SDK didn’t even give you the opportunity to use Interface Builder, you were forced to create your user interface in code.
Now we reach the part of the blog entry where I should tell you how to make an iPhone app without NIBs. Alas, this gentleman has already done a far better job of it than I would have. So you can instead read what he wrote, secure in the knowledge that someone else produced a solution, while I produced a bunch of hot air and whining. You’re welcome.
I’m now working on the user interface for my first iPhone app. I have several views set up, all completely NIB-free. It’s far preferable to create a user interface element in 10 lines of code, rather than making yet another soul-destroying 20-minute slog through Interface Builder. I suppose it’s possible that I’ll run into some situation that forces me to reconsider, but at this point, I’m willing to go far, FAR out of my way to avoid having to invite that sad piece of software back into my life, thank you very much.
Categories: cocoa, iphone, mac, objective-c, programming
Comments Off
let me be your piggybank
February 5, 2009 11:16 amFor many years, I was the primary Allen Brunson on the internet. Searching for my name, roughly 18 of the top 20 hits were all me me me.
Fast forward to the present. The internet is now mainstream. A much larger percentage of the populace has some sort of presence here. It turns out that there are a lot more Allen Brunsons than I would have thought.
Some time in late 2008, the Facebook page for some other Allen Brunson became the number one hit for our shared name. I’m not proud of it, but I have to admit, I was miffed. I’ve spent decades using computers, generating shareware projects and comment threads and zine articles and websites and so on, but this kid gets the top spot? Hrrmph.
Lately I’ve been making more of an effort to advertise. This blog is proof of that. It might be working? As of this writing, I’ve moved above the Facebook guy.
For posterity, here’s the top ten google hits for today:
1) Generic page at LinkedIn for all Allen Brunsons. They must be doing well these days.
3) The rival Allen Brunson on Facebook. I also have a page there, but not many friends, so my Facebook page doesn’t appear anywhere in the top 50 hits.
4) My author page at actionscript.org, for the lone Flash article I wrote.
5) My profile page at BeBits. I haven’t logged into that in years.
6) A letter to the editor I wrote on Salon. I wish this one would sink a little lower.
7) A page at classmates.com for an Allen Brunson that went to high school in Mobile, Alabama.
8) A page at pipl.com that’s generically for all Allen Brunsons everywhere, apparently. A few of the details there are from me, but most aren’t. It’s like they made a composite of all Allen Brunsons? A tad disturbing.
9) Another generic page at reunion.com that is again for all Allen Brunsons. I see the guy from Mobile on there, but not me.
10) My page at FriendFeed. I got flattered into signing up, but I hardly ever use it.
I guess I’m thinking about this right now because I’m attempting to make the transition from boring corporate jobs to interesting contracting and startup work. People that are doing what I want to do all have ubiquitous online presences, establishing strong reputations. Sorry, all you other Allen Brunsons, for hogging so many of the top spots, but I need the exposure. Heh!
Categories: internet culture
Comments Off
bash: timing long-running processes
February 3, 2009 1:44 pmThe innertubes have spoken. According to the search terms I’m seeing in my access logs, you interwebbernauts want to see more bash articles. Very well, then.
Every now and then I write a bash script that might take an hour or more to run. It’s nice to know exactly how long it took, so you can plan accordingly when you want to run it the next time. So I formalized some bash timing concepts I’ve been using for years and packaged them up into a code library you can use in your own scripts.
If you’re just interested in the code, you can skip to the bottom of this entry and cut-and-paste the parts you want into locations of your choosing. If you’ve got a minute, I’ll explain some of the non-obvious bits.
bash functions can only return small integers, typically not larger than 255 or so. They are intended for use as values you’d return to the shell, when the script is finished running. If you want to write a function that returns a string or a large integer, you’re out of luck. The way I get around that one is to write functions that use echo to print a string to stdout. The output can be captured by a calling function using this rather awkward syntax:
returnValue=$(functionName)
Unfortunately, variables in bash scripts are by default global, and therefore available to all functions. I don’t like that very much, so my local variable declarations are all prefixed with the keyword local, which is supposed to make them visible only within the current function. I have a sneaking suspicion that supposedly local variables can still “leak” into other functions, however. I suppose I should investigate that situation and use it as the basis for a future blog post.
Finally, you can use the source keyword to pull functions from one script into another, like this:
source timerlib.sh
Now, on to the code. First up is timerlibtest.sh, a script that demonstrates use of the timerlib functions.
#!/bin/sh # timerlibtest.sh -- test for timerlib.sh # by allen brunson allen.brunson@gmail.com february 3 2009 # pull in timerlib functions source timerlib.sh # this function shows how to time a long-running process function main() { # save current time at the beginning local time=$(timerStart) # put whatever you want to time here echo "starting long-running process ..." sleep 2 # display how long it took timerStop $time "elapsed time:" } # execution start main $*
Here is the text for timerlib.sh:
#!/bin/sh # timerlib.sh -- functions for timing long-running operations # by allen brunson allen.brunson@gmail.com february 3 2009 # return seconds since 1970 # does not work on some unix variants. check 'man date' for details function timerCurrent() { date "+%s" } # inputs a number of seconds, outputs a string like "2 minutes, 1 second" # $1: number of seconds function timerLengthString() { local days=$((0)) local hour=$((0)) local mins=$((0)) local secs=$1 local text="" # convert seconds to days, hours, etc days=$((secs / 86400)) secs=$((secs % 86400)) hour=$((secs / 3600)) secs=$((secs % 3600)) mins=$((secs / 60)) secs=$((secs % 60)) # build full string from unit strings text="$text$(timerLengthStringPart $days "day")" text="$text$(timerLengthStringPart $hour "hour")" text="$text$(timerLengthStringPart $mins "minute")" text="$text$(timerLengthStringPart $secs "second")" # trim leading and trailing whitespace text=${text## } text=${text%% } # special case for zero seconds if [ "$text" == "" ]; then text="0 seconds" fi # echo output for the caller echo ${text} } # formats a time unit into a string # $1: integer count of units: 0, 6, etc # $2: unit name: "hour", "minute", etc function timerLengthStringPart() { local unit=$1 local name=$2 if [ $unit -ge 2 ]; then echo " ${unit} ${name}s" elif [ $unit -ge 1 ]; then echo " ${unit} ${name}" else echo "" fi } # useful for testing timerLengthString function timerLengthStringTest() { local days=$((86400)) local hour=$((3600)) local mins=$((60)) local secs=$((1)) timerLengthString 0 timerLengthString 20 timerLengthString $(( ($hour * 3) + ($mins * 1) + ($secs * 52) )) timerLengthString $(( ($days * 1) + ($secs * 14) )) } # synonym for timerCurrent function timerStart() { timerCurrent $* } # display final elapsed time # $1: value returned from an earlier call to timerStart() # $2: optional descriptive string, such as "total wait time:" function timerStop() { local desc=$2 local secs=$((0)) local stop=$(timerCurrent) local text="" local time=$1 if [ "$desc" != "" ]; then text="$desc " fi secs=$(( $stop - $time )) text="$text$(timerLengthString $secs)" echo $text }
Categories: bash, programming
Comments Off
more meta
February 2, 2009 9:44 pmAt the time of this writing, one of my entries is the second google hit for “ideas bash scripts unix” and “complex bash scripts”. According to my access logs, a couple of people have found me that way. Cool!
Unfortunately, the spammers also found me, in record time. So I’ve currently got comments pretty thoroughly locked down. I think I’ve allowed people who have created accounts here to comment, and nobody else.
I’m not trying to discourage discussion, I just don’t like having to clear the crud out of my blog two or three times a day. Until I get the hang of this, you are encouraged to email me at the address on my “About” page.
Categories: meta
Comments Off

