In iOS 6, Apple added support for Auto Layout, a system for defining the layout of your views using a series of mathematical constraints. While it is possible to make use of Auto Layout completely from source code, there are many ways in which Interface Builder makes this task much easier. Xcode can automatically create constraints for you, as well as show you both missing and invalid constraints.
Since then, Apple has continued to significantly improve Xcode and Interface Builder. Auto Layout is now even easier to use in Xcode 5. And in Xcode 6 Apple has gone above and beyond by adding support for multiple size classes and live custom view rendering. It is obvious to me that Apple really wants you to use Interface Builder as much as possible.
That being said, I have had a difficult time fully embracing XIBs and Storyboards despite all of their glory. I have always known that I should probably be using them, but I kept running into certain situations that would cause me to go back to my trusty old source code. However, Xcode 6 has made enough advances that I have decided to give them another shot.
One of the first things I tried to do was to create a small, custom UIView subclass whose subviews and layout were defined in a XIB file. This should be easy, right? I quickly discovered that it was not at all intuitive. Let’s walk through the process of how we might do this.
First, let’s create a new Xcode project using the Single View Application template. Now, let’s create a new file. Select View from the iOS User Interface section and name your new view MyCustomView
. It doesn’t really matter what we put in here. Let’s make it simple and add a label and a text field as follows:
Now let’s add a new Objective-C class file. Name it SCCustomView
and make it subclass UIView
. The first thing we should do is add the IBOutlet
properties to the class header file.
Set the File’s Owner of MyCustomView.xib to SCCustomView. Then, connect the label and text field outlets as you would for a view controller. Now, we have to worry about two ways that we might want to instantiate an instance of SCCustomView
:
[[SCCustomView alloc] initWithFrame:CGRectZero]
UIView
object and then setting its class to SCCustomView. This will call -initWithCoder:
on our view subclass.Given this, we now need to write some code to load the XIB whenever an SCCustomView is instantiated. We can do this in SCCustomView.m.
Let’s step through this code a little. First, note that we are overriding both -initWithFrame:
and well as -initWithCoder:
. This is very important to enable us to instantiate this view both from code as well as from a Storyboard or XIB. Then, we run some common initialization code that loads the top-level objects from our XIB file. We iterate over this array to find our view. The rest of the code in this class simply adds the view to our hierarchy and creates some constraints to ensure that this view fills our custom view class.
It’s a tad convoluted, but it makes sense and is the best way we currently have for creating a custom view subclass that is backed by a XIB file. If you were going to create multiple UIView
subclasses this way in the same application, the above code could be shared in a common superclass.
If you would like to see Xcode support this and handle the heavy-lifting behind the scenes, feel free to duplicate radar 17292752 at the Apple Bug Reporter.
]]>UIFontTextStyleHeadline
while the content of that post could use UIFontTextStyleBody
.When I began developing Patient IO, I knew that it was going to be important to support Dynamic Type. We wanted the experience of our app to be great and accessible, even for individuals with poor eyesight. From Apple’s documentation, it became apparent to me that the recommended way to support Dynamic Type was to make full use of the various UIFontTextStyle
values and to have the app listen for and respond to changes in the user’s text size preference. What wasn’t apparent was which pieces of text in my user interface should use which style. This was further complicated by my designer having his own opinion on how the various labels in the app should look.
I started down the painful path of trying out each text style at various text sizes and comparing them to the designs I was sent. I would then attempt to choose the style that was most like the one the designer had chosen. However, this was a time-consuming process that did not create a result that made either of us happy. In certain scenarios the choice of style seemed completely arbitrary or even contrary to the name of the style. I was using caption fonts in labels that shows subtitles and caption fonts in labels that showed body text. The more I used these styles the more I felt like they were created with certain types of apps in mind like blogging apps, twitter apps, and e-mail clients. So instead I decided to try and handle the text-sizing myself without using UIFontTextStyle
at all.
iOS allows you to query for the user’s current text size directly using code like the following:
At this point, it becomes relatively trivial to create our own method for determining what font sizes our application should be using at any given time. I opted for a method that took four parameters:
smallestSize
— The smallest font size the text should be.normalSize
— The standard font size assuming the user has never changed their font size preference.maxNormalSize
— The largest the font size can be assuming the user has not turned on Larger Text in the iOS Accessibility settings.maxAccessibilitySize
— The absolute largest size this text can be if the user has enabled the Larger Text accessibility setting.The code ended up looking as follows:
To me, this method felt more flexible and powerful than trying to scale across all content sizes using just a smallest and largest text size. It allowed me to take certain labels and only scale them in a single direction. It let me make the maxNormalSize and maxAccessibility size the same in situations where the text didn’t need to scale to incredibly large sizes. It let me control and avoid situations where the max size of UIFontTextStyleBody
was much larger than a particular screen could adequately handle. And maybe most importantly, this method gave me the ability to exactly match the mockups I had been given using any font I liked and still make the text in the app scale appropriately given the user’s text size preference.
If the iOS-defined text styles make sense for your particular application, you should certainly use them. However, if you find yourself struggling and trying to force the text styles into an app that wasn’t designed for them, consider using the fonts that work best for you and then scaling the font sizes as I did.
]]>In FlightTrack, it is critical that we show accurate dates and times to all of our users, no matter what the cost. If, for some reason, we told a user that their flight was going to take off at 10:00am when it was actually taking off at 9:00am, they could miss their flight and be charged a painful airline rebooking fee.
The importance of showing accurate dates and times extends beyond just flight tracking apps. Any application that needs to display dates and times to its users should consider the ramifications of possibly showing inaccurate information, especially those that download dates from a server.
Take the following JSON data that a server API could send to a mobile client:
This is a perfectly valid ISO 8601 date string that references an exact point in time. While at first glance it may look like it contains time zone information, don’t be fooled. -06:00
refers to the UTC offset of that date string. It does not refer to an actual time zone. That date may be associated with an event in the America/Chicago
or America/Mexico_City
time zones (among others). What is the difference? For one, two similar time zones may have very different rules concerning daylight savings time (DST).
Daylight savings rules can change often. In 2007, the Unites States extended daylight savings time due to the Energy Policy Act of 2005. And things can get even more complicated when daylight savings time is not involved. In 2011, for example, the Pacific island of Samoa jumped forward in time by an entire day.
All of this time zone information (both current and historical) is contained in the tz database, which ships with most operating systems. So, if I wanted to display a time like “10:00am CDT” to a user using the America/Chicago
timezone, I could do the following in Objective-C (which makes use of the system tz database):
What happens if the system has an old version of the tz database? Well, your apps might start showing incorrect dates and times to your users. In addition, the time zone abbreviation strings might be incorrect. Thus, the above code could incorrectly show “11:00am CST” instead of “10:00am CDT”. In the world of mobile apps, having an outdated tz database might not be as uncommon as you think. The tz database is updated with the operating system. Some users don’t update their devices. Others can’t due to carrier lockdown or because they are using an older device model that can’t run the latest and greatest version of its operating system. Unfortunately, these devices can very possibly have outdated tz databases and thus display incorrect dates and times to your users.
So what can we do to fix this? Your gut reaction might be to have your app download the latest version of the tz database whenever it changes. This is certainly something you could do, but it has interesting quirks. As far as I can tell, there is no way in Java or Objective-C to take a downloaded tz database and tell the system frameworks to start using it. This means that you would have to write (or find) code to understand and parse the tz database and then use that information to create explicit NSTimeZone
objects using offsets from UTC.
A slightly different approach (that we opted for) was to send time zone information along with each date from our server:
While quite a bit more verbose than just the ISO 8601 date string, this extra data gives us the ability to display the proper date, time, and time zone strings no matter how old and inaccurate the tz database. This would then change our Objective-C code to the following:
This code is not that much more complicated that the original code. The biggest change for our application was replacing many of our NSDate objects with a different object that stores the extra time zone information associated with each date. This works great when the dates and times are associated with a particular time zone (in our application, the take off and landing dates of flights are associated with the airport where that event takes place). This solution doesn’t work as well if you have to support arbitrary time zones with each and every date. In this scenario, you may be better off opting for the “parse an updated version of the tz database” solution.
Unfortunately, NSDateFormatter
and SimpleDateFormat
are slow. Really slow. In FlightTrack, we have to download many dates from our data providers. In fact, a single request to get data for 100 flights can result in the application downloading and parsing as many as 1,400 date strings. Parsing these ISO 8601 date strings using NSDateFormatter
takes a non-trivial amount of time on a mobile device (approximately 350ms on my iPhone 4S).
Now we could use a faster date parser written in C (especially since we only have to support the ISO 8601 format). However, since we are already adding so much extra information to each date in the response, why not add a little bit more to make it absolutely trivial for apps to parse this date? We can do this by also sending integers representing the number of seconds since the unix epoch.1
This gives us the following data structure:
At this point, all you have to do to parse the date is call [NSDate dateWithTimeIntervalSince1970:unixTime]
, which takes effectively no processor time. If all your clients start using the unixTime
property to construct their date objects, the server doesn’t even need to send the ISO 8601 date string anymore. However, servers are free to send one data type, the other, or even both (this behavior could depend on a request parameter sent by the client). I try not to worry about the increase in response size since servers should all be sending their JSON strings gzipped, anyway.
Depending on the range of dates your application supports this may or may not be an easy thing to do. For example, if you have dates ranging from 200 million BC to 200 million AD, this can be tricky and you have to make sure that you use types that can hold this range without overflowing. ↩
The first thing we can do is add our own support for @YES
and @NO
. To do this, launch Xcode and ensure that the code snippet library is visible by selecting View -> Utilities -> Show Code Snippet Library
and then filtering the list down to your user snippets by selecting “User” from the dropdown. Now, in your code editor, type [NSNumber numberWithBool:YES]
, select the text, and drag it over to the code snippet library. This will create your first code snippet that should look like the following:
As it stands, this snippet won’t really do anything. We need to assign a completion shortcut to it, and while we do that we might as well edit it and clean it up a little. Hit the edit button and set the title to [NSNumber numberWithBool:YES]
, the completion shortcut to @YES
, and the completion scope to “Code Expression”. Your snippet should now look like the following:
Now it is time to save your code snippet and try it out! All you have to do to type [NSNumber numberWithBool:YES]
is to type @YES
followed by <return>
to accept the suggested completion. This is not quite as good as native support for Objective-C literals, but it saves about the same amount of typing.
You can repeat the above process for @NO
, but I won’t go over that here. Instead, let’s try and make it easier to create NSNumbers that hold ints, doubles, and floats.
Getting this to work is going to require a more-complicated code snippet as the snippet will need to support a single parameter. First, let’s create a code snippet for NSNumber’s numberWithInt:
method by typing [NSNumber numberWithInt:<#(int)#>]
into our code editor and dragging that text to the code snippet library. The <#(int)#>
construct tells Xcode that this snippet takes a single parameter and to display (int)
as a hint to the user.
Change the new snippet’s title to [NSNumber numberWithInt:]
, its completion shortcut to @int
, and its completion scope to “Code Expression”. Now, type @int
into your code editor and hit <return>
. You should see [NSNumber numberWithInt:(int)]
appear in your editor with (int)
already selected so that you can immediately start typing the value of your integer. Now you can repeat the same process for @double
and @float
. It’s not quite the same as the Objective-C literals, but it should tide us over until Xcode 4.4 is released.
If you want to take these snippets one step further, you could complete @array
to [NSArray arrayWithObjects:<#(id), ...#>, nil]
and @dict
to [NSDictionary dictionaryWithObjectsAndKeys:<#(id), ...#>, nil]
. However, it’s a pain to create all of these code snippets one after the other. Wouldn’t it be nice to be able to just drag them all into Xcode from a library of useful code snippets? Well you can!
Xcode stores all user-defined code snippets in ~/Library/Developer/Xcode/UserData/CodeSnippets
. By default, each snippet is named using a universally unique identifier. However, it appears that you can rename the code snippet files to whatever you want and Xcode will still load them. This makes it much easier to manage your snippets by giving them descriptive names and storing them in a Git repository somewhere.
All of my code snippets (including the ones in this post) can be found on GitHub. To clone the repository directly to the appropriate directory, run:
Or, if you’d rather, just copy all of the .codesnippet
files inside the repository directly to your ~/Library/Developer/Xcode/UserData/CodeSnippets
folder.
I hope these code snippets will make your lives a little easier until Xcode 4.4 comes out. If you have any other snippets you find invaluable, let me know on Twitter!
]]>Today, we at Mobiata are announcing MBRequest, a simple, open source networking library for iOS and Mac OS X. It uses a blocks-based API built on top of NSURLConnection and NSOperation. MBRequest follows the style of Apple’s CLGeocoder class to create simple, easy-to-use classes that encapsulate the entire network request. The goals of MBRequest are as follows:
We hope you like it! Be sure to check it out over on its GitHub project page.
]]>There have been many times when I have wanted to slightly customize the behavior of an object that is completely owned by its parent. This can be an incredibly difficult task to accomplish. For example, when I wanted to add a background image to a UINavigationController’s navigation bar, I unfortunately found myself resorting to method swizzling to accomplish my task. Now don’t get me wrong, I know method swizzling can be dangerous, and, if you utilize it, you need to be very careful to test your code before each and every release of iOS or OS X. However, if used sparingly and carefully, it can be a powerful, last-resort technique for altering a class that you can’t easily subclass.
Yet I recently found another way to modify objects that appear unmodifiable. There is a lesser-known feature of NSKeyedUnarchiver that lets you change the class of an object that has already been allocated and initialized. NSKeyedArchiver and NSKeyedUnarchiver are generally used to serialize objects to disk; but they can also be used for other purposes.
Going back to the UINavigationBar background image problem, what I really wanted to do was subclass UINavigationBar and tell the UINavigationController to use that class for its navigation bar. However, since navigationBar
is a read-only property of UINavigationController there appeared to be little I could do. NSKeyedUnarchiver can help!
In the code above, setClass:forClassName:
tells the NSKeyedUnarchiver to actually use the SCNavigationBar class whenever it tries to decode an object of class UINavigationBar. Since SCNavigationBar is just a subclass that I created of UINavigationBar, the unarchiving process works without a hitch.
So, by the end of that code block, nc
points to a navigation controller with an instance of SCNavigationBar for its navigationBar
property! This allows us to customize the behavior of the navigation bar to our heart’s content without having to resort to method swizzling. The one catch is that this technique will only work for container classes that conform to the NSCoding protocol. Luckily for us, most of Apple’s classes do.
Because I find this technique to be much cleaner than method swizzling, I have updated my ExampleNavBarBackground sample project to use NSKeyedUnarchiver, instead. Happy coding!
]]>In this post I hope to show you just how bad the standard localization process is for iOS and Mac OS X apps, and how we have found a way to make it much easier for developers to localize their apps and then maintain these localizations and translations over time. In addition, I’ll show you how you can easily share your translations across multiple apps and platforms. This will save your company money that would otherwise be spent duplicating your translation efforts, especially if you are developing for both iOS and Android.
The standard localization and translation process for strings in iOS and Mac OS X apps can be whittled down to the following key steps:
Go through your source code and replace all strings that need to be translated with appropriate calls to the NSLocalizedString
macro. For example, you would replace @"Hello World!"
with something like NSLocalizedString(@"Hello World!", @"A comment for the translator about this particular string.")
.
Run the genstrings
command line tool to extract all of the strings from your source code into one or more .strings
files.
Run the ibtool
command line tool to extract all of the strings found in your various XIB files.
Send the files created in steps (2) and (3) to each of your translators. They will edit these files, replacing English strings with their appropriate translations. When they are done, the translators will send these files back to you.
The translated .strings
files from step (2) should be included directly in your project. The translated .strings
files from step (3) should be used by ibtool
to generate a bunch of additional XIB files which should also be included in your project.
Manually go though all of your newly generated XIB files to make sure that your interface still looks good, especially in languages that tend to be a more verbose than English. Make adjustments to the layout of elements in these files as needed.
Yes, this is the standard process that Apple recommends for localizing and translating your apps. I, however, would not recommend this process to anyone, as I believe it to be fundamentally broken for a number of reasons.
genstrings
is a very basic tool. It simply reads your source files and generates one or more .strings
files. It does not account for any translated strings that you may already have. For example, say that you released version 1.0 of your app with support for 11 languages including English. This means that you have 10 translated .strings
files that you shipped with your app. Now, let’s say that you want to release 1.1 which adds 12 additional strings that you need translated. What are you going to do?
You could run genstrings
again to create a completely new .strings
file for your translators, but there’s no easy way to communicate that they only need to pay attention to those 12 new strings. It’s possible one of your old strings changed slightly, too. Manually adding the 12 strings to your old, translated files is not especially productive. Some translation companies have systems that will help manage all of this for you, but many do not.
After creating translated XIB files with ibtool
(step 5 above), I have often seen it recommended that developers edit each of those new XIB files to make any necessary modifications due to differences in strings lengths. These modifications can be anything from changing font sizes to completely resizing or rearranging some of the views. Doing this places too much knowledge of minute layout differences in your XIB files, which can make your applications hard to understand later. This brings us to the golden rule of working with XIBs: Never manually edit the files generated by ibtool. If you need to make modifications to the layout of your XIBs due to differences in string lengths, do so in code (awakeFromNib
is a great place for this). I think it is much easier to manage a single block of code that modifies layout than it is to manage 18 XIB files.
genstrings
assumes that the English text for your string is also the string key, which is a horrible convention. What if you need to fix a misspelling in your 1.0.1 release? The translated copies of this string are most likely fine, but if you modify the English text (which resides in your source file), you are also modifying the string’s key. So, to fix a simple typo, you literally have to edit your source code to fix the string, and then update the string key in each of your translated .strings
files. If you support 12 languages this means editing 12 files to fix one typo!
There is another reason why using the English text as the string key is wrong. How do you handle words that mean completely different things in different situations? For example, take the word “list”. You could use it as a noun (the title above a UITableView
displaying a list of items) or as a verb (a button title describing the action of listing items on an auction site). While these may be the same word in English, they will most certainly not be the same words in other languages.
However, there is one important thing to realize about switching up your string keys: Apple did not implement a fall-back system for .strings files. This means that if a string key can not be found in your French Localized.strings
file, the system won’t use your English translation, even if it exists. Instead, your users will just see the string key. So, everything seems to work as you would expect if the English text and the string key are the same, but once you change it up and use an actual key for your string, you have to ensure that all of your .strings
files always contain all of your strings, whether or not they are actually translated into the appropriate language.
If you’ve used genstrings
before, you may have noticed how it handles finding the same string with two different comments. You are given the following nice warning message:
Warning: Key "Hello" used with multiple comments "First Comment" & "Second Comment"
If I use the same string multiple times in my project, I have to worry about whether or not I am using the same comment each time? I understand the importance of the warning message here, especially if the two comments are giving contradictory information, but as a programmer it’s not something I should have to worry about.
This brings us to an unfortunate fact about the “standard” way of localizing your apps: all of your string data is spread out across countless source code and .strings
files. Wouldn’t it be nice if they were all centralized in one location?
We decided that we needed a better way to manage our strings. We looked into a few applications and scripts that claimed to help with this painful process, but couldn’t find anything that we liked. Instead, we decided to build our own. Twine is an open-source command line tool written in Ruby that manages all of your strings, comments, and translations in a single text file. Twine parses this file and then generates all of the .strings
files for you. Or, if you’re building an Android app, Twine can generate Android .xml
string files.
Not only does Twine generate .strings
and .xml
files, but it parses them, too. If your translation team is more comfortable editing Android files, just generate them and send them to your translator. When the translator is done, Twine can identify any strings that have changed and write them back to your master strings data file.
With Twine, we have complete control over the generation of these .xml
and .strings
files. We don’t have to worry about our developers or translators forgetting to escape a double quote or forgetting to put a semicolon at the end of each line. And, if we have a change we need to make to a number of strings across all of our languages, we have a single, easy place to do so.
For example:
[yes]
en = Yes
da = Ja
de = Ja
es = Sí
fr = Oui
ja = はい
ko = 예
[no]
en = No
da = Nej
de = Nein
fr = Non
ja = いいえ
ko = 아니오
[path_not_found_error]
en = The file '%@' could not be found.
comment = An error describing when a path on the filesystem could not be found.
[network_unavailable_error]
en = The network is currently unavailable.
comment = An error describing when the device can not connect to the internet.
Twine also supports using only a subset of your strings when generating the output file. This way, you can have multiple apps across multiple platforms share the same strings data file. At Mobiata, we share the same data file across FlightTrack, FlightBoard, and Expedia Hotels. Sharing a string across apps and platforms is as easy as editing a single line in your master strings file.
We are very happy with Twine and hope that you like it and can find a use for it in your own projects. You can find more information about Twine (including a lengthy README) on its GitHub page. Or, you can install it right away as a Ruby gem.
]]>In the past, I have played around with Wordpress, Movable Type, Drupal, Joomla, Byteflow, Geeklog, Django-Mingus, and a handful of others, and the one aspect of all of these systems that kept coming back to haunt me was maintainability. I would invariably want to customize the system by adding some basic functionality I needed and hiding a ton of other features that I didn't need. This was easy enough to do at first, but maintaining these changes after each and every system update was truly an exercise in patience. Some software needed to be updated way too often due to security issues. Others stagnated while their dependencies changed and grew. And in addition to keeping the website software itself up-to-date, I had to keep my eye on Apache, MySQL, PostgreSQL, PHP, mod_wsgi, Python, Ruby, and countless small gems and modules.
Another aspect of these systems that always concerned me was scalability. I either had to find a complex system of intertwining caches to make my site even more convoluted, or I had to come to grips with the fact that being linked to by Daring Fireball, Reddit, Hacker News, or Digg would probably bring my webserver to its knees.
So I recently began to ask myself if my website was really complex enough to warrant any of this hassle. I really only have a handful of blog posts and standalone pages, as well as a few files I want people to be able to download. With DISQUS handling comments, I couldn't think of any content I wanted to serve up that was truly dynamic. Even though static website generation has been around forever, it seems to be gaining more and more popularity, lately, probably due to some of the issues I have mentioned above. So that is when I decided to check out Jekyll.
Now I know there are a ton of alternatives out there, but I largely decided to go with Jekyll because it is simple and yet does everything I currently need. And because so many of these static website generators seem to be founded on the same basic principles, it should be relatively easy to transfer my templates and content from one to another if I ever decide to switch. Well, at the very least it should be easier than any of my prior migration efforts.
Moving to Jekyll was relatively painless. I basically threw together a few templates, touched up my existing blog posts (which were all written in HTML or Markdown), and ran the jekyll
command. There is something incredibly satisfying about knowing how each and every piece of HTML on your website was created, and knowing that you can tweak everything easily and painlessly without having to worry about upgrade issues. That's just the benefit you get with something as simple and easy to grok as Jekyll. And other than my webserver, there is nothing to keep up-to-date! No big upgrades looming around the corner that will force me to reimport my data or migrate my template changes or reapply my software tweaks. And there are a number of other benefits that immediately became apparent, as well:
1. Adding new pages is a snap. Doing something as basic as adding a sitemap.xml file or tweaking an RSS feed seems to be a difficult task in many of these pieces of blog and CMS software. They either do way too much and are difficult to modify or they do way too little and you end up writing a complicated plugin to do something very basic. Creating a site map and RSS feed for my new site took less than five minutes.
2. Defining custom metadata fields for things like HTML keyword
and description
tags is painless. I define the variable at the top of each blog post and then just reference it in a template file. So fast! So easy! And it should be for something so basic. I can now do something in minutes that used to take me much, much longer.
3. My content, templates, and everything else related to my website all reside together and are version controlled together in the same Git repository. I find this to be a wonderful benefit. Before, my content was rarely version-controlled, and when it was, it used some custom, database solution implemented by the blog or CMS. Having these as just regular old files in a Git repository is a wonderful breath of fresh air. And maybe I'm just getting old and crotchety, but I kind of like editing these posts in a basic text editor instead of using a fancy, blinking administration page on my website (though I have seriously considered using MarsEdit, and probably would have tried it recently if it worked with Django-Mingus).
In general, I find the simplicity of Jekyll extremely compelling. I guess the real question will be the test of time. Will it be more maintainable than anything else I have used before? My gut says it will.
]]>Updated 2012/03/05: I have found a better way to solve this problem without method swizzling. Please take a look at my post titled Subclassing Those Hard-to-Reach Classes as well as my ExampleNavBarBackground project which uses this new technique.
Updated 2011/10/18: As of iOS 5, Apple has officially added support for setting background images in UINavigationBars. To do this, all you need to do is execute the setBackgroundImage:forBarMetrics:
method of UINavigationBar.
However, if you want to support both iOS 4 and iOS 5, you will need to conditionally call the code above on iOS 5 and the code I describe below in iOS 4. This is easy to do, and my sample project has been updated to work on both versions of iOS.
Toward the end of the development phase for the first release of Epicure, Adam Betts started sending me stellar design mockups. Many of his ideas were quite easy to implement. However, something as simple as adding a background image to a UINavigationBar was much harder than it should have been. You'd hope that as a class integral to iPhone UI development, UINavigationBar would have a setBackgroundImage:
method. Unfortunately, it does not (at least not as of iPhone OS 3.1).
In the rest of this post, I will describe how, with a little cleverness, we can in fact configure our UINavigationBar to have any background image we desire. And to whet your appetite a little, this is how the final product will look:
Before we dive into the code, it is important to understand the techniques we will be using. When augmenting core Cocoa classes, there are a few different ways one can approach the situation. First (and most simple) is subclassing. It would be great if we could subclass UINavigationBar and then just configure our UINavigationController to use our custom image. Unfortunately, the navigationBar
property of UINavigationController is read-only and there is no way to set it during initialization.
Another way we can modify core Cocoa classes is to use Objective-C categories. These allow us to quickly add new methods to existing classes. By using a category, we could easily add our own setBackgroundImage:
method and call it whenever we create a UINavigationBar or UINavigationController. This method would just add a UIImageView) as a subview to the navigation bar and then send that subview to the back of its superview.
However, if you try this you will quickly see that it only kind of works. The UIImageView is definitely visible and even initially at the correct z-index. However, once you push or pop a view controller onto the navigation controller's stack, you will see that the background image is no longer in the background. Instead, it blocks out the title and navigation bar buttons. This is definitely not what we want.
What we do want is a way to ensure that no matter what happens, the UIImageView we add is always in the background. To do this, we can inject our own code into the sendSubviewToBack:
and insertSubview:atIndex:
methods of UIView. And in order to do this, we can use a technique called method swizzling.
At its heart, method swizzling allows us to target particular methods of existing classes and then override or augment them. There are many existing implementations for swizzling out there, so I would definitely recommend reading about them on the CocoaDev wiki. In the code that follows, I will use a very simple implementation that will work just fine in our situation.
So let's jump into the code. First, we create a single class called SCAppUtils
with a single method:
Notice how we set the tint color to something that makes sense for our image. Then, we add our background image (only if it isn't already there) and ensure it has a z-index of 0 and a tag that no one else is likely using. This tag will be how we access the image view later. Also notice how we don't even need to use a category to do this. UINavigationBar is a subclass of UIView, so we can just add the background view directly.
The reason we create a utility method instead of overriding a core UINavigationBar method like drawRect:
is because we really do not want to override every navigation bar in our app. We want to selectively determine which ones have the new background image. Otherwise, you get into nasty situations where the navigation bar of your UIImagePickerController also has your custom background image. iPhone apps have been rejected for less.
Next, we need to use a category combined with swizzling to augment UINavigationBar's sendSubviewToBack:
and insertSubview:atIndex:
.
This code is a little confusing, and most developers unfamiliar with swizzling will immediately point at it and declare "infinite loop"! However, with the way that swizzling works, we will swap the pointers to the original methods with our 'sc' counterparts. Thus, the methods you see above will actually become the real implementations for the sendSubviewToBack:
and insertSubview:atIndex:
selectors, and calling scInsertSubview:index:
or scSendSubviewToBack:
directly will actually invoke the original methods defined by Apple. So to say it one more time a slightly different way, if you call [myNavBar sendSubviewToBack:otherView]
, you will actually be calling my method above, which will then call [myNavBar scSendSubviewToBack:otherView]
, which is now Apple's implementation of the method.
Phew.
But let's not get ahead of ourselves. Until we actually perform the swizzling, none of the magical stuff I just described will happen. So let's define a utility method for swizzling:
This code is beyond the scope of this post. If you would like to understand how it works, I again refer you to this page on method swizzling.
Finally, we need to call our swizzleSelector:ofClass:withSelector:
method to actually perform the swizzling. We can do this by adding some code to our main.m file:
And there you have it! That should be all you need to do to add a custom background image to your UINavigationBars. Just make sure that you call [SCAppUtils customizeNavigationController:myNavController]
whenever you create a UINavigationController object. What this exercise originally helped me realize is that Objective-C is an incredibly powerful language; even if Apple does not give you the hooks you need, you can most likely do anything you want by being resourceful.
You can see all of this code in action by downloading this sample project on GitHub.
]]>First and foremost, my prompt needed to be fast. In addition, I wanted to show as much information as possible using the fewest number of characters. As such, I decided that in addition to the current branch, I also wanted to know when the current working directory was dirty, as well as whether or not the current branch was ahead or behind of its associated remote tracking branch. This is how my prompt ended up looking:
In the rest of this post I will walk through exactly what needs to be done to replicate my Git prompt.
The first thing we need to do is create a ~/.zsh/functions/
directory to house any snippets of zsh code we will need. Inside that directory, create a file named update_current_git_vars
. This function will be used to set a few environment variables which will make it easy to build up our prompt.
After the execution of this script, up to three environment variables will be set:
$__CURRENT_GIT_BRANCH
— Set to the name of the current Git branch.$__CURRENT_GIT_BRANCH_STATUS
— Used to signify the status of the current Git branch. In short, it will tell us if the local branch is ahead, behind, or diverged when compared to the appropriate remote branch.$__CURRENT_GIT_BRANCH_IS_DIRTY
— Set to 1
if the working directory is dirty.We can now use these environment variables to put together the text for our prompt. Create a file named prompt_git_info
inside of your ~/.zsh/functions/
directory and then save it with the following code:
This will construct the portion of our prompt that shows the current Git repository information. It displays the name of the branch as well as a special character or two for extra information. Here are a few examples:
(master)
— Currently on branch master
.(1.x↓)
— Currently on branch 1.x
, which is behind the associated remote tracking branch.(bug51↕)
— Currently on branch bug51
, which has diverged from its remote tracking branch.(feature3↑⚡)
— Currently on branch feature3
, which is ahead of its remote tracking branch. In addition, the working directory is dirty and thus has uncommitted changes.At this point, we need to ensure that these zsh functions are executed at the appropriate times. We really do not want to run update_current_git_vars
before each and every prompt. Instead, we will try to limit the number of times this code is executed. At the very least, we will need to update the prompt after changing to a new directory. We do this by creating a file named chpwd_update_git_vars
in the ~/.zsh/functions/
directory. Save it with the following line of code:
Later, we will configure zsh to execute this particular function each and every time the current directory is changed.
We should also run update_current_git_vars
whenever a git command is executed. This way, the prompt will update itself immediately after a fetch, commit, push, or pull. Unfortunately, there is no post-execution hook in zsh. However, we can simulate this by using both the pre-execution hook as well as the pre-cmd hook. The pre-execution hook will see that we are about to run a git command and set a temporary environment variable which the pre-cmd hook will see. This second hook can then run update_current_git_vars
right before the prompt is displayed.
In our ~/.zsh/functions/
directory, create a file named preexec_update_git_vars
and save it with the following contents:
Then, in the same directory, create a file named precmd_update_git_vars
and save it with the following code:
This should be sufficient for our needs. The main scenario that is not covered here is the dirtying of our working copy. If we edit a file with vim, TextMate, or any other editor, the prompt will not automatically update itself to display this dirty status. I think that this is acceptable, especially since it would be too difficult to determine when the working copy had been dirtied without resorting to calling update_current_git_vars
before each and every prompt. That, and a simple git status
or git diff
will ensure that the prompt is back up-to-date again.
We are now done creating zsh functions and simply need to hook everything together. Ensure that all of the files created in ~/.zsh/functions/
are flagged as executable. Then, add the following to your .zshrc
file:
A few notes about these .zshrc
entries:
\e[0;34m
to signify blue text when instead they could type %{${fg[blue]}%}
? Yeah, it's still not the most readable piece of text in the world, but at least it's a little better.%B%~%b
displays the current working directory in bold.prompt_git_info
function we created earlier.%{${fg[default]}%}
. This is just a nice way to default the prompt back to however the user has configured their terminal.And that's all there is to it! It's quite a bit of code, but I am happy with the way it turned out. I would hardly consider myself an expert in shell scripting, so if you see any ways in which my scripts can be improved, please leave a comment!
You can view and download all of this code at once in this gist.
]]>And just like that I decided that I needed to put together a blog built on top of Django. I immediately considered Byteflow, but quickly determined that I wanted something much simpler. I didn't need a ton of features; I just wanted a basic blog that I could hack at to help learn more about Django. That is when a friend pointed me at Django-Mingus.
Django-Mingus was released a few months ago by Kevin Fricovsky as a project he was presenting at Django-NYC. It has grown a little since then but at its heart it is still a collection of reusable Django applications wrapped inside a pretty theme. Perfect! Exactly what I needed to dive into the world that is Django.
This will hopefully be a place where I can discuss my current products and projects, post snippets of interesting code, and occasionally write about whatever programming-related topic is currently on my mind. And with that I wrap up my obligatory first post.
]]>