Custom UIWebView Navigation Controller
The next version of NetSketch will include a community browser, allowing you to view uploaded drawings, watch replays, and leave comments without leaving the app. When I started working on the community interface, I looked to other apps for inspiration. Almost every app I’ve used on the iPhone use a sliding navigation scheme, giving you the feeling that you’re drilling down into content as you use the application. This interface is intuitive in a lot of contexts, and dates back to the original iPod. The Facebook app allows you to browse other people’s Facebook pages and uses a drill down navigation bar. This works well for the social-network space because you can drill down to look at information and then return to the first page quickly.
I decided to use a UINavigationBar and implement a similar drill-down interface for the community part of NetSketch. However, I didn’t want to create custom controllers for each page in the community. I wanted to be able to improve the community without updating the app, and I didn’t want to write a communication layer to download and parse images and custom XML from the server.
Using a UIWebView seemed like the obvious choice. It could make retrieving content more efficient, and pages could be changed on the fly. With WebKit’s support for custom CSS, I could make the interface look realistic and comprable to a pile of custom-written views.
I quickly realized that it wasn’t all that easy to implement “drill down” behavior with a UIWebView. Early on, I ruled out the possibility of creating a mock navigation bar in HTML. Since Safari on the iPhone doesn’t support scrolling iframes or “position:static”, “position:fixed” CSS tags, there was no good way to make the bar sit at the top of the screen while allowing the user to scroll. I decided that a native UINavigationBar would be more practical and provide a better user experience. However, the default UINavigationController class was built to use separate controllers for each layer, and doesn’t worry about freeing up memory as the hierarchy grows. Since web pages can be very large, I decided that I should follow suit with Mobile Safari and keep less than eight in memory at once.
I tried several solutions, and finally created a custom DrillDownWebController class with a manually managed UINavigationBar to handle the interface. Here’s a video of it in action:
The DrillDownPage is a wrapper for a UIWebView that acts as its delegate and provides higher-level access to important properties of the page, such as it’s title. I decided to pull this functionality out of the controller because it reduced confusion when multiple UIWebViews were being loaded. When the user clicks a link in the top level web view, a new DrillDownPage object is created and it begins loading the requested page in an invisible UIWebView. The controller locks the current page and displays an activity indicator in the top right corner of the navigation bar. When loading is complete, it animates a slide to the new page and updates the navigation bar. All the other pages in the page “stack” are notified that their position in the drill-down hierarchy has changed.
The notification step is important, because Page objects need to perform cleanup operations if they are far up the hierarchy. To save memory, they take an image of their UIWebView and release it, displaying the image as a placeholder. If the user backs up the hierarchy and the page becomes visible, a progress indicator is displayed and the actual page is reloaded.The DrillDownWebController provides several features in addition to the basic drill-down behavior. Meta tags on the HTML pages can be used to add a button to the top right of the UINavigationBar and program it to load a new URL. Custom url handlers can be used to perform custom code in the controller when the user clicks links in the page. This functionality was used in NetSketch to make netsketch:// links execute custom code and play drawing replays.
Also, standard <a> links in a page cause a drill-down animation when the next page is loaded. Form submissions do not cause a drill down. Tiny forms can be used to make some links drill down and others reload the existing page in the hierarchy.I’ve posted source code for the DrillDownWebController as well as an example. I think it will be useful to anyone who wants to implement a drill down interface without writing a whole lot of code.
Drill Down Example + Controller Source (1.6MB .zip)