Mar 20, 2010
I’m adding note taking to iWoman 2.0 and so I was thinking which metaphor would be one that users would understand and like. So I decided to mimic the look and feel of the built-in Notes app.
There where quite a few interesting things I had to learn and figure out and in this article I am going to share them with you. These are techniques that you can use in many other scenarios besides of making your own Notes view controller.
I was clear from the start that I needed to use a UITextView for the editing itself. Notes.app has several specialities that we have to figure out if we want to capture the look.
- Font is Marker Felt Thin, Size 19.
- each line sits on top of a grayish blue horizontal line
- the text view has a padding at all sides, something that the standard UITextView does not give us
- There is padding at the top, but still the text goes up to the corner
- the horizontal lines move together with the text and never end towards the bottom
- two static vertical brown lines line up with markings at the top and the bottom
- the body of the notes is not just a yellow gradient, but has some structure and speckles
- scrolled text disappears behind the images for the top and bottom edge
- The text view needs to be dynamically resized when the keyboard appears or disappears to prevent hiding of text.
Those where the challenges, in this YouTube video you see my solution and an overview of how I achieved it. More in-depth reasoning you find below.
Mar 14, 2010
Being present longer than iPhone OS exists on the Mac platform NSPredicate was only introduced to us iPhone developers in Version 3.0 of the SDK. They have multiple interesting uses, some of which I am going to explore in this article.
You will see how you can filter an array of dictionaries, learn that the same also works for your own custom classes. Then we’ll see how to replace convoluted IF trees with simple predicates. We’ll explore how to use predicates to filter entries of a table view and finally peek into the inner workings of predicates.
Being simple and powerful at the same time it took me 3 hours to write this article. I hope you don’t give up halfway through it, because I promise it will be a great addition to your skillset as iPhone developer.
Mar 07, 2010
The first thing to learn when starting to persist data onto the iPhones solid state drive is that all apps have their own sandbox. Contrary to other operating systems where you have a shared documents folder, you have several directories for each individual apps. Now on the iPhone these sandbox directories all get a GUID in their name, so you have no way to hardcode or guess the real path they will end up on.
Luckily there is a method of getting the path for those directory, which I wrote about about a year ago: Getting Standard Paths. Today I will elaborate a bit on what I learned since then, it turns out that this is only half the story and as intermediate programmer you will want to use the correct kind of folder for each task.
Feb 28, 2010
Yesterday I sent my first app off to Apple containing an InAppPurchase (IAP). It’s a free app that gives the user an option to pay a dollar for a premium features. That’s what they call Freemium these days. Free to try, premium to get some more.
The possibility for Freemium was only introduced in October 2009 when Apple finally gave in to developer’s wish to be able to do away with those dreaded Lite versions which have a very low conversion rate (about 1%) anyway. Until that time IAPs where only available for paid apps.
After developing on the premium content for about two weeks I hit my first roadblock. You apparently cannot configure an app id for use in a provisioning profile it it is already configured on another developer’s account.
If you are looking for a full walkthrough, this is not the article to provide that. Troy Brant has the most complete IAP Walkthrough on his blog. Instead this is a summary of my mental notes.
Feb 19, 2010
One day of the week I hope to do something that’s not really business oriented, I call it my “Friday-Project”. This title comes from my time in IT where you would generally avoid do any major changes to the systems you are maintaining on Fridays because nobody likes to work on fixing these on the following weekend. So Friday is a great day to just experiment, prototype or dream. At Google they are supposedly giving their employees 20% of their working time for such projects, so I am dedicating as much time to playful exploration as well.
Today I want to explore and hopefully finish some code to upload an image to TwitPic. So the first step is to have a look at their API documentation which is not very pretty, but at least it’s complete. There we see that we want to implement the uploadAndPost function.
Use this method to upload an image to TwitPic and to send it as a status update to Twitter.
Fields to post in post data should be formatted as multipart/form-data:
- media (required) – Binary image data
– username (required) – Twitter username
– password (required) – Twitter password
– message (optional) – Message to post to twitter. The URL of the image is automatically added.
For this we need to construct an HTTP POST request to the URL http://twitpic.com/api/uploadAndPost and construct a multipart body with binary image data, Twitter username, password and an optional message. The response will be XML giving us the URL we can use to link to the picture.
Having the parameters intermixed makes it a bit harder especially if you have never constructed a multipart body before. The easiest method of image uploading is if you can specify parameters in the URL, but the designer of this API thought it smarter to have it in the body. But don’t worry, we’ll get this figured out as well.
Feb 18, 2010
For my current project DTAboutViewController I need to be able to specify tokens in my strings that would be replaced by information about the app at runtime. Of specific interest are CFBundleDisplayName and CFBundleVersion which are both in info.plist. Usually you could hard-code these into your app strings or maybe do a global #define, but for my component I wanted to have the most flexibility with the least amount of work in subsequent project that would use that.
So I put together a category extension for NSString which also shows off how to use NSScanner to find tokens and replace them with values from your info dictionary.
@interface NSString (Helpers)
- (NSString *) stringBySubstitutingInfoTokens;
@implementation NSString (Helpers)
- (NSString *) stringBySubstitutingInfoTokens
NSMutableString *tmpString = [NSMutableString stringWithString:self];
NSScanner *scanner = [NSScanner scannerWithString:self];
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
while (![scanner isAtEnd])
if ([scanner scanString:@"$" intoString:nil])
if ([scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet] intoString:&tokenName])
id value = [infoDict objectForKey:tokenName];
if (value && [value isKindOfClass:[NSString class]])
[tmpString replaceOccurrencesOfString:[@"$" stringByAppendingString:tokenName] withString:value options:NSLiteralSearch range:NSMakeRange(0, [tmpString length])];
[scanner scanUpToString:@"$" intoString:nil];
return [NSString stringWithString:tmpString];
The code works by skipping all characters until it encounters a $, which will be our token identifier. From this position the name of the token is until the first non-alphanumberic character, like a space or period. Then it tries to get a value from the bundle’s info dictionary. If it succeeds the token is replaced in a temporary string, if not it is left unchanged.
Note how I am passing nil into scan functions if I am only interested in skipping characters. If I am interested in the scanned result, then I am passing the address of a pointer to an NSString, that’s the reason for the &tokenName. The scan method creates an autoreleased copy of the scanned string and puts the pointer to this string into the parameter the address of which you have passed. That’s unusual in objective-C and seen more often in pure C code, so be aware of that.
Now with this code I gain a very useful way of displaying copyright information as the section footer of my settings table.
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
return [@"© 2010 Drobnik.com. $CFBundleDisplayName $CFBundleVersion" stringBySubstitutingInfoTokens];
This will look like this, the app’s bundle display name is “About” and the version string is “1.0”.
With this method it’s even thinkable to put your own information into info.plist. Like for example the ID of your app which you would use for a direct link to the review page. Some people complain that Cocoa does not have regular expression support, but actually if you know how to use NSScanner properly you can do even more with it than trying to piece together working regexps.
Feb 13, 2010
I am working on the ultimate “About Page” component at the moment. And of course this won’t be complete without a button to follow the developer on Twitter. Tapbots is one company that has the best role model for modeling this. They have this little button here at the bottom of their about page:
There is something amazing that happened when I tapped on this “Follow us on Twitter” button: it opened up the tapbots user right in Tweetie 2! I was astonished at first, but then it dawned on me that on the iPhone you can have your app respond to a custom URL scheme and obviously some Twitter clients support going to the user’s profile page. I have only Tweetie 2 installed and incidently this supports just that.
Feb 10, 2010
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…
Feb 03, 2010
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);
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];
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.