Adding a Background Image to UINavigationBar
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 bar. 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. 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.
Epicure 1.0.1 Released
I am happy to announce that the first minor update to Epicure (and Epicure Lite) has hit the iTunes Application Store. This version should fix a couple of crashes which could occur in low-memory situations. Enjoy! And as always, please send me an e-mail or leave a comment if you have any questions.
A zsh prompt for Git users
After starting to use Git a few months ago, I thought it would be useful to show the branch of the current repository in my zsh prompt. I did some searching online, but I could not find an appealing solution. Everything I found was either too slow or just didn't show the correct information. So I figured I might as well just throw one together myself.
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 to1if 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 ~/.git/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 branchmaster.(1.x↓)— Currently on branch1.x, which is behind the associated remote tracking branch.(bug51↕)— Currently on branchbug51, which has diverged from its remote tracking branch.(feature3↑⚡)— Currently on branchfeature3, 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 togeter. 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:
- While it is not necessary to autoload the colors module, it makes things much easier. Who would ever want to use
\e[0;34mto 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. - In my particular prompt,
%B%~%bdisplays the current working directory in bold. - The Git portion of the prompt is printed in yellow. This can be easily changed by editing the end of the
prompt_git_infofunction we created earlier. - Finally, we use the colors module again to set the text color back to normal with
%{${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!
EDIT: Moved all code into a single multi-file gist.
Welcome!
In programming, if you want to learn about something, use it.
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.
