We recently released a new iOS app for organizing workout timers called Extimer. The app design called for a menu that the user could access by sliding the main view away or tapping a button. This type of navigation is becoming increasingly common because it allows an app to present a set of menu options in a place that is quick to get to when you need it but stays out of the way the rest of the time.
iOS Sliding Sidebar Menu Requirements
Our requirements for the slider were somewhat specific. It had to:
- Support any type of view in the root or sidebar (e.g. not just a TableView)
- Support opening and closing with both a tap and a swipe gesture
- Be extremely responsive, even on older devices and when the views contained animation or transparency
- Follow Apple's guidelines for container views
I looked around for an existing open source component which would do all of those things but was ultimately unsatisfied with the available options. Typically the deal breaker involved some kind of restriction on the content of the views or an inability to include our own custom view controllers. We already had much of the functionality for the menu and main view controllers built, we just wanted to display them in a sliding sidebar. It would have been very messy to repurpose one of the existing components to do this. So, I spent some time creating a new reusable sidebar container which would meet all of the requirements above.
Handling iOS Container Views Gracefully
There are two primary components:
The first is
DHSidebarViewController which is responsible for managing the view controller hierarchy and handling user interaction. It provides methods for setting and changing the root view controller (on top) and the sidebar view controller (on the bottom). It also manages the gesture recognizers which respond to the user's swiping and tapping actions.
The second component is DHSidebarLayoutView. This is the primary view of
DHSidebarViewController. The root and sidebar views are added as children of this view.
DHSidebarLayoutView's primary job is to position its child views based on the current
offset value. The offset represents how far to the right the root view should be moved. The sidebar view will also be moved slightly creating a parallax effect.
With these two components it is possible to compose any two custom view controllers into a sliding sidebar experience. Rather than keep this to ourselves, we decided to share it with anyone who might find it useful. You can download DHSidebarViewController on GitHub. It is licensed under the MIT Open Source license.
Contributions are welcome and we'd love to know what you think of this.
Tech Tip: If you use
view.layer.shadowOffset to create a drop shadow for a view you will almost certainly experience a soul-crushing performance hit if you try to move that view around or animate on top of it. The secret is to supply a shadow path based on the view's bounds like so:
1 2 3 4 5
// Add left side drop shadow newRootViewController.view.layer.shadowOffset = CGSizeMake(-3, 0); newRootViewController.view.layer.shadowOpacity = 0.3f; UIBezierPath *path = [UIBezierPath bezierPathWithRect:newRootViewController.view.bounds]; newRootViewController.view.layer.shadowPath = path.CGPath;
Of course, this is mentioned in the documentation for shadowPath but I missed it on my first read-through so hopefully this will help someone else. ;)