Making More of the More View
One of the most frustrating things about using a tab bar in your app is that once you exceed five tabs you get the dreaded ‘More’ view - a system component which always displays with stock styling, lacks almost any customisation options, and just seems to have that slight air of desperation about it. In fact the only real way to get the more view consistent with the rest of your app’s design is to create your own view controller, put it in the fifth slot, then start adding the navigation logic for the rest of your app to that manually (which also prevents users editting the tab bar).
But instead of just rolling over and creating our own custom more view, though, I wanted to go ahead and customise the one that Apple already provided. What’s more, I want do it without crazy runtime hackery, without subclassing anything, and with safeguards against future implementation changes in the system components. So, in this post, that’s exactly what we’re going to do.
How we’re going to do this is actually quite simple: we’re going to replace the
moreNavigationController’s table view’s delegate with one of our own design1.
After I thought of this, I did find a couple of implementations following the same
principle here and there about the web, but they all fell short in a number of ways -
especially in handling potential implementation changes in future versions of
UITabBarController. Ideally, we want to use the original implementation of the tab bar
delegate as much as possible, picking and choosing methods to override to gain our custom
behaviour with as few changes as possible, and if things go wrong we want to gradefully
degrade to the default more view implementation without any crashes or bugs.
Switching out the delegate is a pretty simple job. We’ll make a class which conforms to
UITableViewDelegate protocol, and assign it as the delegate for the table view. The
sketchiest bit is attaining the table view itself, and to guard against the possibility
that Apple might stop using a
UITableView for the More view in the future, I’ve added
a check to make sure it’s the right class. That way, if the default implementation of
this view is changed to
UICollectionView or something, the app will simply fall back
to the default look instead of crashing horribly.
1 2 3 4 5 6 7 8 9 10 11 12
The new ‘proxy’ delegate then holds on to the original delegate and forwards any messages it receives directly on to the original delegate. Run this up and you’ll find your more view controller acting exactly like it did before2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
All that remains now is to start implementing table view delegate methods to make
alterations to the design of the table view. This is why the above
YES if instances of the current class respond to the selector
- otherwise if we tried to implement a
UITableViewDelegateProtocol method that the
original delegate didn’t implement, our code would never get called.
The most useful method to override here to alter the look of the table view is
-tableView:willDisplayCell:forRowAtIndexPath:. Here’s my implementation which changes
the more view to display orange titles in Avenir and pink cell images on black
backgrounds which turn green when selected:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
It’s worth mentioning that each time you override a delegate method it’s always worth also forwarding the call on to the original delegate (assuming it implements that method, of course). Without this, you may inadvertantly alter the behaviour of the view in ways you didn’t expect.
I’m not going to say that the possibilities for customisation are unlimited, but there’s
certainly an awful lot of tweaking which can be done to the cells from inside this proxy
delegate, including alterations to the cell layout, height adjustments, colours and fonts
and even selection behaviour. What’s more, there’s essentially no real reason you couldn’t
pull off the same trick with the table view’s data source, and add custom headers or footers
or even entire cells. And all without ever even having to import
Now, you might say at this point that this is still definitely a runtime hack. And, well, you may have a point. But to my mind it’s a comparatively elegant one, and it doesn’t do anything beyond leveraging the flexibility of the design patterns and the publicly declared protocols in Cocoa Touch.↩
Only in software development can doing a whole load of work to achieve exactly the same functionality we had before we started be treated as an achievement.↩