app store progress
August 6, 2009 6:40 pm
For the first month of its existence, my game was ticking along at about 200 downloads per day. I have no experience with this, but that seemed pretty good to me. Then I released 1.1.0, which is what caused that spike you see near the end there. On the first day: 192 new downloads, 949 upgrades to the new version. Second day: 576 new downloads, 1100 upgrades. It’s tapered off since then, but downloads are still significantly higher than they were before the new version came out.
According to the records I get from Apple, 6707 people downloaded version 1.0.0 of the game. So far, 3936 of those people have upgraded to the new version. Let’s hope you are all still so loyal when I get around to releasing the paid version. Heh!
Categories: iphone, itunes app store
Comments Off
NSLog() sucks
July 18, 2009 8:15 amIn a previous installment I explained why printf() sucks, and how I fixed it. Today I am going to focus on NSLog(), which sucks even worse.
Why NSLog() sucks
Here’s a typical NSLog() call:
NSLog(@"hello: %d", 6);
Note that it doesn’t require a newline character at the end of the format string. So we’ve made a little progress over printf(). It’s not until you see the output that we get to what I think is wrong with it:
2009-07-18 08:48:29.067 nslog_sucks[44201:10b] hello: 6
Sigh. Just as with printf(), NSLog() is optimized for a corner case I hardly ever need. The assumption is that I would always want to know when this action took place down to a thousandth of a second, and that I’d want to see the name of the program doing the output, the program’s PID, and so on. The output I’m interested in is drowned out by unimportant noise.
Replacing NSLog() with something better
This is how NSLog() is defined, in NSObjCRuntime.h:
FOUNDATION_EXPORT void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2)));
At first glance, it appears that this would give you the same sort of warning that you get with printf() if the format string doesn’t match the arguments supplied. Sadly, I’ve never been able to coax GCC into supplying warnings in that case. But I added the same __attribute__ decoration to my NSLog() replacement, in case this is just a bug in current versions of GCC that will be fixed at some point in the future.
Finally, here’s the source code for nlog(), my NSLog() replacement that doesn’t fill the screen with unnecessary details. The header file, nslog_sucks.h:
// nslog_sucks.h -- an NSLog() alternative // by allen brunson july 18 2009 #ifndef NSLOG_SUCKS_H #define NSLOG_SUCKS_H #include <Foundation/Foundation.h> // nlog(), a better NSLog() void nlog(NSString* nfmt, ...) __attribute__((format(__NSString__, 1, 2))); #endif // NSLOG_SUCKS_H
And the implementation file, nslog_sucks.m:
// nslog_sucks.m -- an NSLog() alternative // by allen brunson july 18 2009 #include <stdio.h> #include <stdlib.h> #include <Foundation/Foundation.h> #include "nslog_sucks.h" void nlog(NSString* nfmt, ...) { va_list args = NULL; NSString* nstr = nil; va_start(args, nfmt); nstr = [[NSString alloc] initWithFormat:nfmt arguments:args]; va_end(args); puts([nstr UTF8String]); [nstr release]; nstr = nil; } int main(int argc, const char** argv) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; nlog(@"hello from nlog: %s %d", "text", 2); [pool release]; pool = nil; return 0; }
Categories: cocoa, iphone, mac, objective-c, programming
Comments Off
App Store rite of passage
June 25, 2009 5:21 amI just got my first iPhone app into the store! iTunes link This can be a traumatic experience for many developers, and we all learn more about what the process is like through blogs, so here’s my own war story.
I submitted my app on June 14, 2009. It went live on the store on June 24. Let’s call that ten days. Better than some, worse than others. Plenty acceptable for me.
You’ve no doubt read about the horrors of trying to get your app properly signed for deployment on hardware devices and for distribution through Apple’s store. It certainly happened to me as well. I’m not going to go into all the details, because I followed so many dead ends that I’m not sure what I did anymore. I’ll mention the main thing that bit me hard, though. My app’s name has a space in it. When Xcode expanded the ${PRODUCT_NAME} variable into my app’s identifier, it replaced that space with an underscore. This worked fine for ad hoc builds, but the app store flatly refused to accept it. The error message I got didn’t tell me what the real problem was. After much googling and reading other people’s anguished cries, I solved it by removing the underscore. It looks like current versions of Xcode expand spaces to dashes, which is hopefully acceptable for distribution.
I was most worried about HIG violations. I did a lot of things in my app that are not very, um, “Apple-like.” My game has extensive settings, but rather than creating a settings bundle, I put them into a tab on my tab bar. I spent quite a bit of time writing a view controller that uses a UIWebView to display help pages, complete with back and forward buttons for navigation. None of Apple’s apps seem to have any help text anywhere. Apple wants you to use your Default.png to make it look like your app loaded faster than it did, but to me that sounds like lying, so my version simply has the word “Loading…” on it. I created an “about” page. Perhaps worst of all, I created a view that displays the app’s memory usage statistics, which will probably confuse the hell out of most people. I very nearly took that out of the final app. But Apple didn’t give me any trouble about any of that stuff. My app sailed through the approval process with nary a peep.
Based on other people’s stories, here’s some problem areas I might have avoided. I made sure that my artist used more or less the same content for the app’s 57×57 and 512×512 icons. I read somewhere that Apple doesn’t like to see version numbers below 1.0.0, so that’s what I used for my app, although most of my projects seem to languish in the 0-dot-something range. I didn’t use any of Apple’s tab bar graphics, because they all have predefined meanings that don’t match what my tabs are for. I didn’t use any undocumented API calls, although I was sorely tempted a few times. I guess that was enough to keep me out of trouble, in this particular case.
Categories: iphone, itunes app store
Comments Off
printf() sucks
June 23, 2009 1:44 pmI am a big fan of printf()-style debugging. It helps you get an overview of a problem that traditional debuggers are not so good at. So it’s a bit unexpected that I do not like printf() itself.
Why printf() sucks
printf() is optimized for a weird corner case that you almost never need. You’ll no doubt recognize this as standard usage:
printf("Hello, World!\n")
Notice that newline character at the end. It’s a pain to type. As best as I can tell, it exists so you can do this:
… which causes the output from both printf() calls to be printed on the same line. Swell. How many times have you needed to do that? I first started programming in C in the late eighties, and my lifetime total so far is zero. I typed thousands of unnecessary newline characters before I finally wised up and wrote a replacement.
Format strings are error-prone
The most likely problem you’ll have with printf() and functions like it is a mismatch between the format string and the variables presented to it. For example:
printf("Two strings: %s %s\n", "text");
The format string calls for two strings to be printed, but you’ve only provided one. The call to printf() might work fine, crash, or print weird results, depending on what happens to be lying around on the stack. If your compiler is GCC — and it probably is, if you’re programming for any UNIX variant, including Mac OS X — there is a good workaround. Here’s my definition for echo(), my printf() replacement:
void echo(const char* tfmt, ...) __attribute__((format(printf, 1, 2)));
That weird __attribute__ business is a GCC-ism that means “this function works like printf(), and here’s the argument numbers to use for the format string and the variable args, respectively.”
This feature doesn’t work unless you specifically enable the proper GCC warning. If you’re using makefiles or the command line, pass -Wformat to the compiler. If you’re using Xcode, bring up the project information window. In the “Build” tab, there’s a section called “GCC 4.0 – Warnings.” The warning you want is labeled “Typecheck Calls to printf/scanf,” which should be enabled. Once you do that, then when you write code like this:
echo("bad format: %s %s", "text");
The compiler will give you this warning:
warning: too few arguments for format
… which saves you from the undefined behavior your program was about to be subjected to.
(C++ introduced cout, which is a printf() replacement. It handily works around the format string issue discussed here. I’ve always felt that cout introduces more problems than it solves, so I personally avoid it.)
Functions you can use
The sample code that follows includes three functions you might want to use in your own programs.
echo() — works exactly the same as printf(), except it doesn’t require a newline at the end of its format string.
sfmt() — works exactly the same as echo(), except it puts the formatted contents into a std::string object, rather than printing to stdout.
fmtArg() — useful if you want to build your own printf()-like function similar to echo() or sfmt(). Have a look at how echo() uses it, which should be enough for you to get started.
Example code
First the header file, printf_sucks.h:
// printf_sucks.h -- printf() alternative // by allen brunson june 18 2009 #ifndef PRINTF_SUCKS_H #define PRINTF_SUCKS_H // sfmt() and support functions std::string fmtArg(const char* tfmt, va_list args); std::string fmtArgLarge(int32_t byteCount, const char* tfmt, va_list args); std::string sfmt(const char* tfmt, ...) __attribute__((format(printf, 1, 2))); // echo(), a better printf() void echo(const char* tfmt, ...) __attribute__((format(printf, 1, 2))); #endif // PRINTF_SUCKS_H
Now the source file, printf_sucks.cpp:
// printf_sucks.cpp -- printf() alternative // by allen brunson june 18 2009 #include <assert.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string> #include "printf_sucks.h" void echo(const char* tfmt, ...) { va_list args = NULL; std::string text; va_start(args, tfmt); text = fmtArg(tfmt, args); va_end(args); puts(text.c_str()); } std::string fmtArg(const char* tfmt, va_list args) { static const int32_t kBufferSize = 2 * 1024; // the extra four bytes are to guard against buffer overruns char cbuf[kBufferSize + 4]; int32_t size = 0; assert(tfmt && tfmt[0]); size = vsnprintf(cbuf, kBufferSize, tfmt, args); if (size < kBufferSize) { return std::string(cbuf); } else { return fmtArgLarge(size, tfmt, args); } } // called when fmtArg() didn't have a big enough buffer std::string fmtArgLarge(int32_t byteCount, const char* tfmt, va_list args) { char* cbuf = NULL; int32_t clen = byteCount + 10; int32_t size = 0; std::string text; cbuf = static_cast<char*>(malloc(clen + 4)); if (!cbuf) return ""; size = vsnprintf(cbuf, clen, tfmt, args); assert(size < clen); text.assign(cbuf); free(cbuf); cbuf = NULL; return text; } int main(int argc, const char** argv) { echo("hello from echo: %s %d", "text", 2); return 0; } // works like echo(), but puts the formatted contents into a std::string std::string sfmt(const char* tfmt, ...) { va_list args = NULL; std::string text; assert(tfmt && tfmt[0]); va_start(args, tfmt); text = fmtArg(tfmt, args); va_end(args); return text; }
Categories: c++, linux, mac, programming
Comments Off
Drawing NSStrings in unusual rotations
June 1, 2009 8:10 pmIf you’re used to writing apps for desktop computers, the iPhone’s screen can seem awfully small. You must make creative use of every pixel available to you. One way to do that is to draw some text vertically, rather than horizontally, which I decided to do for an iPhone app I’m working on right now.
For drawing normal horizontal text, UIKit has a category called UIStringDrawing that provides convenient methods for drawing the contents of an NSString to the current graphics context, like drawAtPoint:withFont: and drawInRect:withFont:. Sadly, UIKit does not provide methods for drawing text in any other orientation except standard horizontal. I will now present an NSString category you can add to your own iPhone projects that allows you to draw vertical text from bottom to top, top to bottom, or horizontal text upside down.
My category supports the same three UITextAlignment options that UIKit provides for left, right, or centered text alignment. If using my drawInRect:... method to draw a string that’s too wide to fit within the rectangle supplied, the right end of the string will be truncated to fit, with an ellipsis character added.
UIKit does a better job of handling long strings than my code does. UIKit can wrap text to two or more lines, for example, which I did not try to emulate. The UIKit methods also allow you to provide a UILineBreakMode enum value, which gives you fine-grained control over how strings are truncated. I personally don’t need that much flexibility, so my category has no such support.
Finally, there is one last limitation of my category that might be a deal-breaker for you. The standard UIKit string-drawing functions can be used to display any Unicode character, so long as the font you’re using has a glyph for it. My category is limited to the roughly 255 characters present in the MacRoman character set. That means my code is good for displaying text in English and most European languages, like French and German, but it is completely unsuitable for text in, say, Chinese.

I’m aware that this is an outrageous limitation. You’d be hard-pressed to find a programmer who is more gung ho about Unicode than I am. I struggled mightily for several days trying to find a way around this. Sadly, this is due to the way that the underlying CoreGraphics drawing routines work, and there is no good way around it that I can see. I’ll cover this in more detail later, in case you’re interested.
Using the WBTextDrawing category
I’ve included a sample Xcode project that demonstrates the use of my WBTextDrawing category. Download the project by clicking here.
I’ve provided a screen-shot, but it isn’t much to look at. There are five strings displayed onscreen. The four strings at the edges of the view are displayed with my own WBTextDrawing category, drawn in all four available orientations. The string in the center is drawn with one of UIKit’s own drawInRect:... methods. The lines in red show the bounding rects used to draw the five strings. The four strings around the edges all have an associated green pixel, which illustrates the draw point used to draw that particular string. Naturally you wouldn’t draw the green and red bits in a real app. I added them to this demo so you’ll have a better idea of what’s going on.
Almost all the code in the project is boilerplate that can be safely ignored. To add my string-drawing category to your own program, copy WBTextDrawing.mm and WBTextDrawing.h out of this project and into your own. All other source files presented here are for demonstration purposes only.

You will note that WBTextDrawing.mm ends with an mm extension, rather than the usual m. That’s because this source file must be compiled as Objective-C++, due to the fact that it contains a small amount of C++. I know many Objective-C programmers have a strong aversion to C++, and believe me, I understand! But the underlying CoreGraphics method that’s used to draw rotated strings insists on being given a const char*. It does not work with NSString objects directly. So I chose to convert NSString objects to std::string objects just before drawing them. Yes, I could have accomplished this without leaving the confines of Objective-C, but std::string seems to me like the best tool for the job.
Specifying text drawing locations

UIKit’s UIStringDrawing category contains several drawAtPoint:... methods for drawing NSString objects. The point you pass to these methods is the far left end of the font’s baseline, as illustrated by the green point in the first figure. For the sake of compatibility, I chose to use this same convention for the drawAtPoint:... method in my own WBTextDrawing category. No matter what drawing orientation you’re using, the specified draw point is always the far left end of the font’s baseline, relative to the string being drawn. This is also the way the low-level CoreGraphics drawing routines work, conveniently enough. See the second figure for what this looks like when drawing a bottom-to-top vertical string.
UIStringDrawing also has several drawInRect:... methods. In this case, the rectangle supplied is the entire area allotted for drawing the string, illustrated by the red rectangles in figure one and figure two. Again, my own WBTextDrawing category does the exact same thing, for compatibility’s sake.
Text drawn is limited to MacRoman
This is without a doubt the worst limitation of my WBTextDrawing category. I see no good way around it, however.
Whatever method you use for drawing rotated text, you must accomplish two goals: 1) Apply a given font to the current drawing context, and 2) Draw the NSString supplied by the caller. The easiest way I can see to apply a font is to use CGContextSelectFont(). That method gives you two encoding options: kCGEncodingMacRoman, which uses the MacRoman encoding, or kCGEncodingFontSpecific, which means you must provide your own character-to-glyph translations, as far as I can tell. Ahem. UIKit can’t do this by itself, apparently, but the expectation is that us lowly app programmers should be able to do it? As if. So the only real option here is to use the MacRoman encoding, then use CGContextShowText() to display the text. This is the way my category works. Anything outside the MacRoman character set displays as gibberish.
There’s another method you can use to apply a font to the current context: CGContextSetFont(). This method doesn’t take any kind of encoding parameter at all, so it would appear to be immune from the problems presented by CGContextSelectFont(). Alas, once you’ve called that method, CGContextShowText() doesn’t work anymore. The CoreGraphics docs say you should instead call CGContextShowGlyphsAtPoint(). That function expects its caller to supply an array of glyphs, not characters. Which implies that you’ve got some method up your sleeve that will convert characters to glyphs for a given encoding. I don’t know about you, but I don’t have any such method lying around. So I’m stuck with boring old MacRoman.
This seems like an awfully strange limitation to build into the low-level CoreGraphics text drawing routines. If you know of any way around it, please tell me what it is, so I can update my text-drawing category appropriately.
Categories: cocoa, iphone, objective-c, programming
Comments Off
Beta testers wanted
May 14, 2009 1:42 amMy blog traffic has grown exponentially. I am as baffled as anyone that one of my entries is the top google hit for “cocoa task_info”, as an example. Seems like a lot of people found me that way, and at least some of you are now RSS subscribers.
So, perhaps a few of you would like to help me beta-test my almost-finished game? I set up a new subsite for it, here: Hearts Solo. I am especially interested in hearing from people who have iPhone 3G or iPod Touch devices, because I haven’t tested my game on either of those. I got a lot of really good beta testers for the two big releases of my USENET newsreader, so I’m hoping I can do as well this time.
One of my goals for this project is to scare up some new iPhone work. I’m not currently working on anything that’s making me any money. If you need some iPhone or Mac programming done, I’d love to hear from you! allen.brunson@gmail.com
Categories: cocoa, iphone, programming
Comments Off
linux: app memory usage
May 10, 2009 5:39 pmOnce upon a time, I wrote a Linux app in C++ that used the database access libs from a well-known corporate database vendor. The app was very important to the employer I wrote it for. It had to run 24 hours a day, seven days a week. Unfortunately, the database access libs leaked like a screen door on a submarine. I did not have the source code for them and the vendor was not responsive to my pleas.
So I set up the app so it could restart itself every few hours, in order to clear its leaks. The app ran inside a script, and when it exited with a certain return value, the script would know to restart it immediately. This worked pretty well for a couple of months. Then I discovered that certain usage patterns would cause the app to completely exhaust its heap in as little as 30 minutes. It crashed, taking a lot of important data down with it.
My next try was a tad more sophisticated. I figured out how to determine how much memory the app was using currently, and it would restart itself when its heap size had grown to about 1.5GB. Now it didn’t matter how long it took for the program to exhaust its heap, it would always restart itself long before that happened.
I had planned to show you source code for getting a Linux app’s resident set size. Seems like a good complement to my earlier blog entry which had code to do the same thing for Apple’s platforms. But it requires a lot of code, due to boring details like file access, string manipulation, and so on. So I’ll just describe the algorithm, and let you implement it yourself, if you’re interested.
Getting a program’s resident set size on Linux involves querying the /proc pseudo-filesystem. First you must create a pretend “filename” that looks like this:
/proc/(pid)/statm
where (pid) is the pid of your program, which you can get with the posix function getpid(), converted to a textual representation. Open the “file” with whatever file manipulation APIs you normally use. fopen() is a good choice. Read the contents of the pseudo-file with fread(), then close it with fclose(). You should end up with a line of text that looks like this:
38546 48861 1677 104 0 36313 0
This is a list of various memory statistics about your program. The only one we’re interested in is the second number, 48861 in this example. This is your app’s resident set size, in pages. I’d prefer to have that value in bytes, so you can multiply the number of pages by the memory page size, which you can retrieve by calling getpagesize(). That’s it, now you’ve got a more-or-less accurate count of the number of bytes your Linux program is currently using.
Categories: linux, programming
Comments Off
cocoa: app memory usage
May 3, 2009 6:30 pmMy least favorite part of Cocoa programming is its reference-counted memory management scheme. If you can exclusively target Mac OS X 10.5 or later, then you can use garbage collection instead, which is better. But it doesn’t work on Mac OS X 10.4 or earlier or the iPhone, so garbage collection might as well not exist, as far as I’m concerned. Yes, I know Cocoa’s reference-counting scheme has “only a few simple rules” you have to follow … yet Apple’s own apps tend to leak pretty badly. Back when I was using Mac OS X 10.4, I could only run Safari for a few hours before I had to restart it. Seems to be less of a problem in Mac OS X 10.5, but they’re likely using garbage collection these days.
So, early versions of your Cocoa programs are probably going to leak. There are ways to combat this. The primary ones are the leaks command-line tool and the Instruments app that comes bundled with Xcode, neither of which I like very much. I’d prefer that the app itself report its bad behavior. To that end, I’d like my apps to be able to tell how much memory they are using.
Surprisingly, an app’s memory usage is subject to interpretation. Suppose your app and another are both using one in-memory copy of a shared framework. Should your app’s memory total include the size of the framework or not? What if your app has a lot of memory allocated that currently lives on disk in a swap file — should you count that?
I’ve spent some time in the past studying this issue, and I’ve decided to go with a figure called the “resident set size.” This is more-or-less how much memory your app is using. Not perfect, but plenty close enough for my needs. Here’s how you can get it.
#include <mach/mach_init.h> #include <mach/task.h> #include <sys/time.h> #include <sys/resource.h> #include <stdint.h> #include <string.h> #include <unistd.h> int64_t MemoryUsage() { task_basic_info info; kern_return_t rval = 0; mach_port_t task = mach_task_self(); mach_msg_type_number_t tcnt = TASK_BASIC_INFO_COUNT; task_info_t tptr = (task_info_t) &info; memset(&info, 0, sizeof(info)); rval = task_info(task, TASK_BASIC_INFO, tptr, &tcnt); if (!(rval == KERN_SUCCESS)) return 0; return info.resident_size; }
This was difficult to write. It makes use of Darwin kernel APIs, which Google knows almost nothing about.
This works on any version of Mac OS X back to about 10.2, I think. It also works on the iPhone simulator, as well as on actual iPhone hardware. I’ve tried it myself on all these, including my own phone. The fact that this function works unmodified on both Macs and live iPhone hardware is proof positive that the two platforms use very similar kernels.
If you want to use this to detect leaks, you have to track your apps’ memory usage over time. Take a snapshot of your app’s size near the beginning of a run, then put your app through its paces for half an hour or so. Is the app’s memory usage trending up?
In addition to being useful for tracking leaks, I simply appreciate knowing how much memory my apps are using. There’s a definite upper limit on how much RAM you can allocate on an iPhone, and there’s no virtual memory at all. If your iPhone app exhausts all physical memory, it can’t start swapping to disk, it’ll just get killed.
I’ve noticed that the app I’m working on now uses 14MB in the simulator, but only 8MB on real iPhone hardware. Probably because simulator apps are really just modified Mac apps. Windows, views, and other user interface elements on the Mac are no doubt heavier than their iPhone counterparts.
Categories: cocoa, iphone, mac, objective-c, programming
Comments Off
iphone: drawing rectangles
April 28, 2009 9:33 amMac/AppKit and iPhone/UIKit development are more alike than different. In general, I’d say the iPhone APIs feel more “fresh” than their Mac equivalents. It seems to me like the current crop of Cocoa engineers at Apple have learned a lot in the years since the company ditched “classic” MacOS for an updated version of NextStep, and they’ve used that wisdom to design this new crop of frameworks. And they’re not afraid to revisit past decisions that didn’t work out so well: they discarded MacOSX’s view-origin-in-the-lower-left-corner mistake and put it in the upper left corner for the iPhone, where it belongs.
On the other hand, AppKit is far more complete than UIKit. Almost anything user-interface-related you might want to do on the Mac is covered by APIs in AppKit. UIKit is missing many of those amenities, likely because it is targeted at resource-constrained devices that don’t have the space for dozens of multi-megabyte frameworks. The practical upshot is that I’ve spent a lot of time writing iPhone stuff that AppKit would have provided for me. All the more reason to write blog posts like this one, so coders can google up the answers rather than spending days reinventing wheels.
I like drawing rectangles. Only rarely do they end up in my final apps, but I use them a lot while debugging, to ensure that a view area I’ve staked out is where it should be. On the iPhone, this kind of thing is done with Core Graphics. It is a very complete framework. You can do anything with it. The down side is that CG code tends to be verbose and complicated, requiring lots of function calls to do the simplest things. This is a situation crying out for wrapper functions.
I’ve written a UIView category that provides methods for drawing just about any type of rectangle you can think of: filled or outlined, rounded corners or square, including point-drawing methods. You can provide a color to draw with, or use the current draw color, which is black, unless you change it. You can draw a translucent rectangle by specifying a draw color that has an alpha value of less than one. You can change the radius of the rounded corners drawn by fiddling with the constant kCornerSize at the top of the implementation file.
This is beginner-level stuff, so I guess I should add that UIKit doesn’t let you draw things whenever you feel like it. (That’s true for all GUI environments I’ve ever programmed for, now that I think about it.) You must draw only within a UIView’s drawRect: method. If this is news to you, you should read one of Apple’s introductory texts before continuing.
Here’s a sample drawRect: method you can add to an existing view to demonstrate the rect-drawing methods:
-(void)drawRect:(CGRect)rect { CGRect test = CGRectMake(0.0, 0.0, 20.0, 20.0); [self strokeRect:test color:[UIColor magentaColor]]; test.origin.x += test.size.width; [self strokeRoundRect:test color:[UIColor yellowColor]]; test.origin.x += test.size.width; [self fillRect:test color:[UIColor blueColor]]; test.origin.x += test.size.width; [self fillRoundRect:test color:[UIColor redColor]]; }
… which produces the output shown in the screen-shot. here’s the header file for the category:
// WBViewRect.h -- rectangle drawing methods for UIView // by allen brunson march 2 2009 #ifndef WBVIEWRECT_H #define WBVIEWRECT_H #import <UIKit/UIKit.h> @interface UIView (WBViewRect) // save and restore graphics context -(void)contextRestore:(CGContextRef)context; -(CGContextRef)contextSave; // points -(void)drawPoint:(CGPoint)point; -(void)drawPoint:(CGPoint)point color:(UIColor*)color; // filled rects -(void)fillRect:(CGRect)rect; -(void)fillRect:(CGRect)rect color:(UIColor*)color; // filled rects with rounded corners -(void)fillRoundRect:(CGRect)rect; -(void)fillRoundRect:(CGRect)rect color:(UIColor*)color; // outlined rects -(void)strokeRect:(CGRect)rect; -(void)strokeRect:(CGRect)rect color:(UIColor*)color; // outlined rects with rounded corners -(void)strokeRoundRect:(CGRect)rect; -(void)strokeRoundRect:(CGRect)rect color:(UIColor*)color; @end #endif // WBVIEWRECT_H
Finally, here’s the implementation file:
// WBViewRect.mm -- rectangle drawing methods for UIView // by allen brunson march 2 2009 #include "WBViewRect.h" #pragma mark module data static const CGFloat kCornerSize = 5.0; static CGRect rectStrokeAdjust(CGRect rect) { rect = CGRectIntegral(rect); rect.origin.x += 0.5; rect.origin.y += 0.5; rect.size.width -= 1.0; rect.size.height -= 1.0; return rect; } static void roundRect(CGContextRef context, CGRect rect, CGFloat ovalWidth, CGFloat ovalHeight) { CGFloat fw = 0.0; CGFloat fh = 0.0; assert(ovalWidth >= 1.0); assert(ovalHeight >= 1.0); CGContextSaveGState(context); CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); CGContextScaleCTM(context, ovalWidth, ovalHeight); fw = rect.size.width / ovalWidth; fh = rect.size.height / ovalHeight; CGContextMoveToPoint(context, fw, fh / 2.0); CGContextAddArcToPoint(context, fw, fh, fw / 2.0, fh, 1.0); CGContextAddArcToPoint(context, 0.0, fh, 0, fh / 2.0, 1.0); CGContextAddArcToPoint(context, 0.0, 0.0, fw / 2.0, 0, 1.0); CGContextAddArcToPoint(context, fw, 0.0, fw, fh / 2.0, 1.0); CGContextClosePath(context); CGContextRestoreGState(context); } @implementation UIView (WBRectView) -(void)contextRestore:(CGContextRef)context { CGContextRestoreGState(context); } -(CGContextRef)contextSave { CGContextRef ctxt = UIGraphicsGetCurrentContext(); CGContextSaveGState(ctxt); return ctxt; } -(void)drawPoint:(CGPoint)point { [self drawPoint:point color:nil]; } -(void)drawPoint:(CGPoint)point color:(UIColor*)color { CGRect rect = CGRectMake(point.x, point.y, 1.0, 1.0); [self fillRect:rect color:color]; } -(void)fillRect:(CGRect)rect { [self fillRect:rect color:nil]; } -(void)fillRect:(CGRect)rect color:(UIColor*)color { CGContextRef ctxt = [self contextSave]; if (color) { CGContextSetFillColorWithColor(ctxt, [color CGColor]); } UIRectFill(rect); [self contextRestore:ctxt]; } -(void)fillRoundRect:(CGRect)rect { [self fillRoundRect:rect color:nil]; } -(void)fillRoundRect:(CGRect)rect color:(UIColor*)color { CGContextRef ctxt = [self contextSave]; roundRect(ctxt, rect, kCornerSize, kCornerSize); if (color) { CGContextSetFillColorWithColor(ctxt, [color CGColor]); } CGContextFillPath(ctxt); [self contextRestore:ctxt]; } -(void)strokeRect:(CGRect)rect { [self strokeRect:rect color:nil]; } -(void)strokeRect:(CGRect)rect color:(UIColor*)color { CGContextRef ctxt = [self contextSave]; if (color) { CGContextSetStrokeColorWithColor(ctxt, [color CGColor]); } UIRectFrame(rect); [self contextRestore:ctxt]; } -(void)strokeRoundRect:(CGRect)rect { [self strokeRoundRect:rect color:nil]; } -(void)strokeRoundRect:(CGRect)rect color:(UIColor*)color { CGContextRef ctxt = [self contextSave]; rect = rectStrokeAdjust(rect); roundRect(ctxt, rect, kCornerSize, kCornerSize); if (color) { CGContextSetStrokeColorWithColor(ctxt, [color CGColor]); } CGContextStrokePath(ctxt); [self contextRestore:ctxt]; } @end
Categories: cocoa, iphone, objective-c, programming
Comments Off
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

