Full Source code: https://github.com/boctor/idev-recipes/tree/master/CustomBackButton
Problem:
Apps like Instagram, Reeder and DailyBooth have a UINavigationBar with custom background and sometimes custom back buttons. How do we build a similar custom UINavigationBar?
Solution:
In our recipe for Recreating the iBooks wood themed navigation bar we added a wood image as a subview of UINavigationBar. This worked but it turns out that we were lucky.
If we had tried to push another view controller onto our UINavigationController we would’ve ended up with a UINavigationBar that has nothing but our wood image. This is because as the standard UINavigationBar implementation adds items, it also then sends them to the back of its view hierarchy. So even if we add our custom image to the bottom of the UINavigationBar view hierarchy, the rightBarButtonItem, titleView and leftBarButtonItem end up below the custom image and out of sight.
The nav bar background
We need a new, more flexible solution: Instead of adding to a UINavigationBar’s view hierarchy we will subclass UINavigationBar. We’ll override UINavigationBar’s drawRect and draw our custom background image directly:
// If we have a custom background image, then draw it, othwerwise call super and draw the standard nav bar - (void)drawRect:(CGRect)rect { if (navigationBarBackgroundImage) [navigationBarBackgroundImage.image drawInRect:rect]; else [super drawRect:rect]; }
Any time navigationBarBackgroundImage changes we also need to call [self setNeedsDisplay] so the new background image is properly drawn.
A couple of methods allow us to set or clear the background image:
// Save the background image and call setNeedsDisplay to force a redraw -(void) setBackgroundWith:(UIImage*)backgroundImage { self.navigationBarBackgroundImage = [[[UIImageView alloc] initWithFrame:self.frame] autorelease]; navigationBarBackgroundImage.image = backgroundImage; [self setNeedsDisplay]; } // clear the background image and call setNeedsDisplay to force a redraw -(void) clearBackground { self.navigationBarBackgroundImage = nil; [self setNeedsDisplay]; }
Multiple nav bar backgrounds
In most cases, you’ll have a single custom background image that you’ll set once.
If you need to change the custom background more than once, say every time the user enters a new section, then simply call setBackgroundWith every time you want the nav bar’s background to change.
The source code for this recipe is one app with a single UINavigationController and a table row for each of the apps: Instagram, Reeder and DailyBooth. When you select a row, we push a view controller that calls setBackgroundWith to set the background image for each app.
Custom back button
In the iBooks recipe we created a custom UIBarButtonItem by creating a button with a stretchable image and adding the button as the customView of UIBarButtonItem.
We do the same thing here except we use an image has an arrow on the left side and we adjust the button’s titleEdgeInsets to center the text properly.
The backButtonWith:highlight:leftCapWidth: convenience method on our CustomNavigationBar class creates a back button which we can then add as the leftBarButtonItem. This replaces the built in back button.
To resize our custom back button to match width of the text, we use UILabel’s sizeWithFont to measure the width of the text and resize the button appropriately.
Trying it out
Just like the standard back bar button implementation, the CustomNavigationBar will set the title of the back button to the title of the previous controller on the UINavigationController stack.
The source code for this recipe also lets you dynamically change the back button text after a view controller is pushed onto the UINavigationController stack. The back button resizes as needed and you get to see and experiment with different text and button widths.
Full Source code: https://github.com/boctor/idev-recipes/tree/master/CustomBackButton
Great post!
Thanks for this!
Great!
[…] This post was mentioned on Twitter by Hacker News YC, Vojto and others. Vojto said: This website is amazing http://t.co/OUDm5B9 #ios […]
[…] intent: IBM Foursquare Launches New Business Pages Formspring forges out of awkward adolescence How do iPhone apps Instagram/Reeder/DailyBooth implement custom back buttons? Tech Bubble: 5 Reasons Why This Time It’s Different Everyday at Vexed we round up the […]
Another great article!
I’m one of your avid reader.. Thanks for sharing! 🙂
Cheers
I always wondered how Instagram customized the back button and nav bar.
Another great article, thanks 🙂
You guys are so awesome.
Awesome post, thanks for sharing with the rest of us devs 😉
Great blog, sharing really caring – so thanks for caring! =)
I was wondering if there is any way to use your recipe for a custom tabbar together with this recipe for a custom navigation bar?
Since the custom nav bar is actually added in the IB, and there is no reference to the VCs inside the XIB for the tabbar I have no idea how to do this.
The navigationBar property of a NavigationController is read-only and I don’t really want to subclass the NavigationController as well. The only solution I see is to replicate your solution for the navigationBar using categories. Though I haven’t tried it yet.
Does any one have any ideas of how to solve this?
Categories is what I used before reading this article.
The biggest downside to using a category is when developing for the iPad. An override would also mess up the background of Navigationbars inside popovers. It’s a bit hard to explain, but I’ve learned to avoid categories for these kind of things. ( Apple strongly discourages you to use this approach as well).
Hi:
I am new in iOS
If I don’t use the IB
When I call the CustomNavigationBar method
the app will crash
I create an UITabBarController and add an UINavagationController
and in the UINavagationController RootViewController ViewDidLoad
CustomNavigationBar* customNavigationBar = (CustomNavigationBar*)self.navigationController.navigationBar;
[customNavigationBar setBackgroundWith:[UIImage imageNamed:@”navigationBarBackgroundRetro.png”]];
it crash, can you help me
sorry my bad English
Hi,
I also facing the same issue.
Based on the error, it happen in the line of [customNavigationBar setBackgroundWith:[UIImage imageNamed:@”navigationBarBackgroundRetro.png”]];
Thanks for any help
I believe it happens when customNavigationBar does not cast correctly to CustomNavigationBar, and remains as UINavigationBar which does not respond to setBackgroundWith:imageNamed:
You should connect the outlets correctly in MainWindow.xib to get the cast operation right. Check the xib file in the original source.
Awesome !
Why doesn’t it work in RootViewController though ? I just added the `setBackgroundWith:` method but it doesn’t seem to do anythingMy bad it’s because of the `willAppearIn:` method.Great result. Unfortunately, using nib files makes the code too opaque for a howto.
It’s easy to figure out the custom UINavigationBar (override drawRect: and draw your own image), but how is the navigation bar used? Usually, the UINavigationController’s navigationBar property is readonly. Randomly clicking through the nib files, trying to find a reference to anything custom didn’t net me anything, I’m afraid.
There are a few other oddities, such as saving the custom image in an UIImageView. Is there a reason this is needed? (As opposed to simply retaining the UIImage.)
Is there any way you can update the code to not use nib files?
Great blog otherwise. Thanks!
Agree, Jesper. Great work, however, I cannot reproduce this for the life of me.
When I push to a new VC from the RootController, the custom image shows up in the nav bar but no back button.
It’s driving me nuts.
To clarify:
Running the example project, everything works fine.
It’s when I import CustomNavigationBar over to my project that things break.
I noticed in the example MainWindow.xib file that the Navigation Controller had a the CustomNavigationBar as it’s NavBar. So i’ve implemented that.
What happens when I go from Root to a new VC, the logo pops up (using all of the instragram example VC code) in the title bar and no custom background or any leftbarbutton. Basically, it looks like the customnavbar is not custom when I push over. No idea what I am setting up impoperly.
Any tips would be appreciated.
Thanks.
[…] here […]
Great post!
On IB, how did you manage to connect navigationController to more than 1 IBOutlet in different files? http://twitpic.com/6wqymc – That’s the only thing that keeps me from implementing your solution on my project. Any ideas?
Did you get any reply ?
How do i get this working if I am not using nib files..
Please help.
– Roshit
[…] (i.e. not baked into the image) using a stretchable image. I originally learned how to do this from iDev Recipes – How do iPhone apps Instagram/Reeder/DailyBooth implement custom NavigationBars with … but I have refactored the code significantly to make it more modular and easy to […]
[…] 参考资料: [1] http://idevrecipes.com/2011/01/12/how-do-iphone-apps-instagramreederdailybooth-implement-custom-navi… […]
[…] 参考资料: [1] http://idevrecipes.com/2011/01/12/how-do-iphone-apps-instagramreederdailybooth-implement-custom-navi… […]
[…] http://idevrecipes.com/2011/01/1 … width-back-buttons/ 回复daiyao: @chen79899 […]