DHSidebarViewController: an open source sidebar navigation controller for iOS

July 24, 2013

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:

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. ;)

Comments

Permalink

Hey Jay,

Your component looks very good - but I have one question..
Why does every side bar open source component requires the Side bar to be the application's root view controller?

I have a login flow that supposed to be finished before I can go to the side bar container VC.

Thanks,
Guy

Permalink

Hi Guy, think of it like the sidebar controller is taking the place of a tab bar controller. When it's being used it will always sit at the top of the view controller hierarchy. Also, due to how the controller is responsible for actually laying our multiple contained views, it isn't necessarily sensible for it to be contained within other views itself.

That is likely why you see that as a common pattern.

That being said, there is nothing stopping you from making your login view controller the root view controller at app launch and then later replacing it with a sidebar view controller.

Just want to report back that you pretty much solved all my problems with the Sidebar!
Still has more testing to do but so far so good...

and didn't to assign key window's rootVC at all... :)

Thanks a lot!!

Permalink

Thanks for this code.

I have a problem now with the Status Bar color. i have a solid green background and before adding the sidebar, the status bar has white text as i expect it to be. But after making the DHSidebarViewController the rootViewController, the status bar text turns black... i have tried many combinations of configurations in my views, and i cant get it to work!!
I would appreciate any tips on this...

Thanks!

Permalink

Hi Jay,

I like your approach and try to implement it within my app. I face the problem that I have 2 Storyboards, the first.storyboard do some login-action and switches then to the second.storyboard, where your sidebar controller should be available.

Adding your code into the application:didFinishLaunchingWithOptions: leads to wired errors as the first.storyboard has no sidebar view controller, as this is first in the second.storyboard available.

Do you have a hint how I could solve this issue? In my opinion I should active your code, if the user enters the second.storyboard but I do not have a glue how I could do that...

cheers -- jerik

Permalink

Hi Jay, thanks for your work.
I'm new to iOS and i'm trying to implement your sidebar, but i'm not sure how to do it. Maybe a more elaborated example on github would be great.

Anyway, the sidebar is working, but when i click on a sidebar button to go to another section of my app, it doesn't work as i expected.

My code is:

-(void)openMainWindow:(NSString *)windowID
{
DHSidebarViewController *rootController = //some long line to get the controller
UIStoryboard* mainStoryboard = //getting the storyboard
rootController.rootViewController = [mainStoryboard instantiateViewControllerWithIdentifier:windowID];
}

that only changes the rootView but does not close the sidebar. I tried with a [rootController closeSidebar] at the end but it get a really weird effect.

Can you help me?
Thanks in advance

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.