If you like my tutorials, you will love my book . It is chockful of advanced programming techniques and the only comprehensive barcode reference for serious iOS developers.
Our DNA is written in Objective-C

Category Archive for ‘Recipes’ rss

Understanding UIFont

Have you ever really TRULY looked at the documentation of UIFont?

I had to, because I was looking for some metrics information that I could use to custom draw UILabels. And if you want to be independent of what font is set, then you have to get certain metrics, but the Apple SDK documentation of UIFont leaves a bit to desire.

When I asked which characters would be good representatives of a font, I was sent a link to this blog “celebrating the beauty of the ampersand”. Fine, an ampersand will be there as well…

Read more

Drawing Rounded Rectangles

Once you get deeper into coding iPhone apps you find that CoreGraphics starts to become a real friend. A friend who lacks in certain areas, because you will still have to piece together some shapes with the shape drawing functions that CG provides.

For sake of reusability you want to put the creation of distinct shapes into their own respective methods. You could make those into C functions like their CG brethren, but for our purposes objC-methods suffice.

This method creates a CGPath for a rounded rect inside the given rectangle with the given radius. We are alternating adding a straight line segment and then a corner by means of AddArcToPoint.

- (CGPathRef) newPathForRoundedRect:(CGRect)rect radius:(CGFloat)radius
	CGMutablePathRef retPath = CGPathCreateMutable();
	CGRect innerRect = CGRectInset(rect, radius, radius);
	CGFloat inside_right = innerRect.origin.x + innerRect.size.width;
	CGFloat outside_right = rect.origin.x + rect.size.width;
	CGFloat inside_bottom = innerRect.origin.y + innerRect.size.height;
	CGFloat outside_bottom = rect.origin.y + rect.size.height;
	CGFloat inside_top = innerRect.origin.y;
	CGFloat outside_top = rect.origin.y;
	CGFloat outside_left = rect.origin.x;
	CGPathMoveToPoint(retPath, NULL, innerRect.origin.x, outside_top);
	CGPathAddLineToPoint(retPath, NULL, inside_right, outside_top);
	CGPathAddArcToPoint(retPath, NULL, outside_right, outside_top, outside_right, inside_top, radius);
	CGPathAddLineToPoint(retPath, NULL, outside_right, inside_bottom);
	CGPathAddArcToPoint(retPath, NULL,  outside_right, outside_bottom, inside_right, outside_bottom, radius);
	CGPathAddLineToPoint(retPath, NULL, innerRect.origin.x, outside_bottom);
	CGPathAddArcToPoint(retPath, NULL,  outside_left, outside_bottom, outside_left, inside_bottom, radius);
	CGPathAddLineToPoint(retPath, NULL, outside_left, inside_top);
	CGPathAddArcToPoint(retPath, NULL,  outside_left, outside_top, innerRect.origin.x, outside_top, radius);
	return retPath;

The method has to be called new-something so that Build&Analyze does not tell you about a memory leak. Having the method name begin with new tells the static analyzer that this method is supposed to return something that the caller has to take care of releasing.

Now, if we want to use this method, then we can do so in any view’s drawRect:

- (void) drawRect:(CGRect)rect
	CGContextRef ctx = UIGraphicsGetCurrentContext();
	CGRect frame = self.bounds;
	CGPathRef roundedRectPath = [self newPathForRoundedRect:frame radius:5];
	[[UIColor blueColor] set];
	CGContextAddPath(ctx, roundedRectPath);

Having the rounded rectangle shape as a path object allows us to reuse it several times. You could for example draw a gradient inside after having used the shape for clipping, then draw a border and also add a shadow. All from the same shape. At the end we just release it to clean up.

UILabels with Neon-Effect

For a customer project I needed to have a Neon glow on some text. Being the geek that I am I would not want to settle for simple creating the effect in Photoshop since I wanted to be able to smoothly scale the text. Custom drawing comes to mind, but where?

One might be tempted to first consider create a new UIView for this effect, but then you’d have to also add all those properties that UILabel has on top of what it inherits from UIView. Second idea was to create a UILabel category, but my experiments have shown if I override a standard method like drawRect in my category then this overrides it for all UILabels.

(Background Photo “Seattle by Night” by Alan Bauer)

So the final – and successful – decision was to subclass UILabel: DTGlowingLabel was born.

Read more

Array Self-Sorting

The current Dr. Touch Part that I am working on for my store is about quickly clustering POIs if they would be too close together on a MKMapView. For this purpose I need to calculate distances between all annotation pins and add to this the distances to all newly found clusters. Currently this involves resorting the whole list of distances multiple times. So I put my thinking hat on and built this category extension for NSMutableArray to add numbers of objects in the correct place according to the specified sort order.

To cut down on search time for the insertion point I am using a “divide an conquer” approach. Split the search range in half and check if the value at this index is bigger or smaller than the one I am trying to insert. Continue to divide until the length of my search range is 0. At that point I have found the appropriate place for the insertion.

This approach means that for 1000 values/objects in the array only 10 comparisons are necessary. This is most likely way less than if you are constantly creating sorted copies of the array with one of the three standard sorting methods.

Read more

Easier Version Checking

Sometimes you may need to modify your app’s behavior depending on which OS version it’s running on. As of XCode 3.2 you can choose a different SDK to compile against than you choose for the deployment target. This allows to make an app that runs as low as 2.0, but uses all the bug fixes in the SDKs. And it forces the compiler to make dynamic links to 3.x frameworks. So you can check at run time if such a framework is available and if not present the feature that depends on it.

When working on DTAugmentedRealityController I needed to limit it’s use on devices with a magnetometer and also which run a version of iPhoneOS that’s greater than or equal to 3.1 because this is the version when Apple introduced the capability of overlaying your own view on top of an UIImagePickerController. So I needed an elegant method, I would not settle for comparing strings. That’s why I came up with this extension to UIDevice.

Read more

Adding Last Build Date & Time

If you need or want the date and/or time the last time your app was built then there are two handy macros you can use. Consider the following example:

char *date = __DATE__;  // e.g. 'Dec 15 2009'
char *time = __TIME__;  // e.g. '15:25:56'
NSLog(@"Build date: %s", date);
NSLog(@"Build time: %s", time);

I tested it with GCC 4.0, GCC 4.2 and even the new LLVM GCC 4.2. It worked fine with all three compilers.

Those are precompiler macros which work by getting the precompiler to replace them with the current date and time when the file in which they are located in gets built. Bear in mind that generally building is incremental and therefore these will only get updated if there really IS a new build happening. That’s a drawback if you forget it, but you can or should always do a build – clean before you build a release or distribution version to make certain all got updated.

Since those macros date back to C times they get replaced with C-style strings. That’s a pointer to a char array with a binary zero at the end to terminate the string. To convert them to obj-C NSStrings is simple by means of the %s formatter or by using one of the more complicated initializers of NSString.

char *date = __DATE__;
char *time = __TIME__;
NSString *myDate = [NSString stringWithCString:date encoding:NSASCIIStringEncoding];
NSString *myTime = [NSString stringWithFormat:@"%s", time];

Finally if you are really so much “pro” that the build time of your app matters, then you will probably also ask if there is a way to force building of certain files to forego the incremental building. Sure you can. All you need to do is to add an extra script to your target to set the last modified time of the file using these macros to the future.

touch -t 2012310000 "${PROJECT_DIR}/Classes/CalendarAppDelegate.m"

Touching Script

It’s not enough to simply set the modified time once because then the next modification sets it back to the current date. Thus the need for this simple unobtrusive script, in this example I am setting the modified time for CalendarAppDelegate.m to Dec 31st 2020 which is sufficiently far away so that this blog article will work for the next 11 years. :-)

How to make a Pull-To-Reload TableView just like Tweetie 2

When I started on Twitter, I tried out a few Twitter clients both on Mac and iPhone until I quickly settled on Tweetie. When Loren Brichter made the bold move to sell Tweetie 2 as a seperate app I also purchased it because I am convinced this guy means quality and Tweetie 2 is on the first page of my springboard.

One thing that’s cool about Tweetie 2 is the fresh paradigm to refreshing the contents of a table view. Up until now we had been looking for space to mount a reload button on, sometimes having to resort to adding an extra tool bar for just one view so that you can have enough space. Now if you have a tableview that it sorted reverse chronologically, then you have a natural urge to make new items appear at the top by pulling down the table with extra force.

Loren recognized this need and innovated the Pull-To-Reload paradigm. If you want to refresh a tableview in Tweetie 2 then you simply pull down the table far enough for an additional cell to appear at the top with the instruction “Pull down to refresh”. If you do, then at a certain point the arrow rotates and the text changes to “Release to refresh”. All accompanied by two distinct wooshing sounds and a pop once the reloading action has ceased. The Intuitiveness of this paradigm is so compelling in fact that people who use Tweetie 2 start to try to refresh ALL tableviews like this.

Might be a good case to make this the standard way from now on because it feels more logical and natural than to tap on a small button with a circular arrow on it. A user of MyAppSales requested that I add this mechanism for reloading reviews of individual apps. At first I thought this to be advanced magic, probably using forbidden techniques. But after a bit of research and lots of hints coming from my Twitter friends (thanks Thomas and Fabian) I figured it out. This article explains how I did it.

Read more

Getting the User's Language

Geppy Parziale¬†wrote about this topic on his “Invasive Code” blog. The method he proposes is to be found in Apple’s documentation and looks like this:

NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:@"AppleLanguages"];
NSString* preferredLang = [languages objectAtIndex:0];

This is a good method to retrieve the set language, BUT it has two major drawbacks when used in regular apps:

  • Changes are not visible to the app by simply changing the language in settings. It seems you also have to change the region for it to be effective.
  • This really returns the user’s set language but for most cases you instead want to know which localization is used.

What good does it get you if you know that the user has set Klingon as his iPhone language? Not much because most of the time you then still want to know which localization is the currently active one.

You know if the iPhone is set to any language that is not amongst your localizations then the auto-localization feature falls back to the app’s default language, usually English. If you add German localization and the user chooses German then this is automatically used.

I learned this the hard way when we added multiple languages to LuckyWheel. Now here’s the method that I found to be way more useful:

NSString *selectedLocale = [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0];

If you read Apple’s page to the end you get to a box where it says:

“Although you can get the user’s preferred settings from the defaults database, it is recommended you use the CFBundleRef functions or NSBundle class instead. The associated functions and methods of those objects return the preferred language or locale that is also supported by your application.”

That’s exactly what I am saying …

UIImage from UIView

This is a neat trick that I developed when I needed a PNG Image with the same content that I was already drawing in a UIView. I had finished the broad strokes of a small app I am developing and then I figured it would be a neat trick to also be able to e-mail a graphic I was drawing as an attached image.

Turns out to be not that hard to do – after a couple of hours of trial and error. Though attaching said PNG file to an e-mail is only something that’s available with SDK 3.0 and above. But that’s another story, let’s stick to the image generation technique I developed. And one more thing: I am NOT taking about the contents of subviews to be put into an image, strictly what’s the result of the UIView’s drawRect method.

Read more

Manipulating UIColors

For the charting class I am working on I wanted to modify a given UIColor by darkening it and also by making it more translucent. This is both straightforward for some aspects but also tricky for others as I have learned.

Looking at the documentation for UIColor you find that there are only several methods provided of creating a UIColor, but none to actually modify it. Neither you will find any colorByModifying… methods, nor is there a UIMutableColor class true to the general rule of keeping mutable and immutable variants for all the important classes.

If you do a bit of Quartz drawing then you also know about CGColor, CG being short for CoreGraphics. Here we have more luck. Actually UIColor is basically a wrapper for a CGColor as I have learned subsequently. By using CoreGraphics functions to modify this internal CG color I was able to come up with two category methods extending UIColor with the missing functionality.

Read more