155

It appears that side-swipe menus are becoming a more common interface element as more information gets crammed into each iPhone app. Facebook has included it in their latest version and the new Gmail app appears to include it as well. I was wondering if anybody had thoughts on the most efficient way of developing something like this as it's becoming a more common interface element. While I have my own thoughts on how to build this, I'm curious to hear what other people think.

Facebook swipe navigation

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Nick ONeill
  • 7,341
  • 10
  • 47
  • 61
  • 5
    Whose Facebook profile did you peek in, Nick O'Neill? :-p – Constantino Tsarouhas Nov 22 '11 at 11:25
  • I've noticed, that when sideswipe menu is activated, you can't do anything with swiped-out part of the view. My idea was to render current view in image and display portion of it, when sideswipe is activated – Denis Nov 21 '11 at 14:21
  • 1
    possible duplicate of [SplitView but on iPhone](http://stackoverflow.com/questions/7775195/splitview-but-on-iphone) – JosephH Jan 14 '13 at 16:53
  • Ooh! Ooh! Don't forget to put one on the right side, too! – paulmelnikow Mar 12 '13 at 00:22
  • 1
    @Zoidberg I'm interested to hear more about this. Personally, I like the sidenav! But I am a developer, not a designer. What makes this UX poor? – Robert Karl Mar 27 '13 at 21:33
  • @Denis I'd like to point out that Facebook doesn't do that, if you scroll down the page and while the page is still scrolling you tap the button on the bar to pull out the side-bar the page keeps scrolling. However, that is a nice idea :) It might free up some memory! – Albert Renshaw May 16 '13 at 02:00
  • iOS 7 too has slide to right feature. Will it cause any problem with this? – Bilbo Baggins Jul 17 '13 at 17:15
  • 3
    Please anyone watch [Designing Intuitive User Experiences](https://developer.apple.com/videos/wwdc/2014/#211-video) to learn about what issues apple has identified with this so-called "hamburger menus" – vikingosegundo Jul 22 '14 at 19:42
  • Check the [AMSlideMenu](https://github.com/arturdev/AMSlideMenu) library. Its fully customizable, currently has no known bugs! – arturdev Jul 22 '14 at 19:32

18 Answers18

84

There is a great library for this by Tom Adriaenssen: Inferis/ViewDeck

It's very easy to use and has a fairly large following.

EDIT:

For something a little more lightweight, check out: mutualmobile/MMDrawerController

It doesn't have all of the features of ViewDeck but is simpler to modify and extend.

Chris Knadler
  • 2,819
  • 2
  • 26
  • 32
  • 6
    I recommend everybody use this. – Almas Adilbek Aug 07 '12 at 09:33
  • 3
    best solution: flexible and simple to use – HotJard Oct 19 '12 at 10:04
  • 2
    I tried it and found a lot of Bugs in their Example. After some fast clicks the viewcontrollers are misaligned – Torsten B Jan 10 '13 at 14:45
  • @TorstenB I've used it for several apps and it always seems to work fine for me. That being said, if you have a reproducible bug, try filing an issue [here](https://github.com/Inferis/ViewDeck/issues). The library is still actively being developed so there is a good chance someone will help you with your issue. – Chris Knadler Mar 17 '13 at 00:21
  • 13
    We've used it for several apps, but it was very difficult to maintain and add new features to it. It also has suffered from trying to implement too much. We decided to write our own called MMDrawerController. Light-weight, and focused: https://github.com/mutualmobile/MMDrawerController – kcharwood May 16 '13 at 01:50
  • 1
    MMDrawerController is very good library .Also it's support universial app. iphone and ipad apps – Erhan Demirci Nov 09 '13 at 10:06
  • ViewDeck is no longer maintained as of November 2014. The issues list is growing constantly. The last real commit was in May 2013. Until they can pick up a maintainer, I recommend new projects use another option. – Kyle Robson Nov 11 '14 at 20:20
48

I created a library for this. It is called MFSideMenu.

Among other things it includes support for iphone+ipad, portrait+landscape, menu on the left or right side, UITabBarController, and pan gestures.

Michael Frederick
  • 16,664
  • 3
  • 43
  • 58
  • Its a great project .. but Its not supporting for landscape mode.. Can u give us a hint to use it in landscape mode too .. Thanks – Sameera Chathuranga Jun 26 '12 at 05:24
  • Thanks @SameeraChathuranga. I hope to add landscape support if I get some time. The only part that doesn't have landscape support is the SideMenuViewController... I believe you would have to do something like the 2nd answer here: http://stackoverflow.com/questions/2508630/orientation-in-a-uiview-added-to-a-uiwindow. Feel free to fork and contribute to the repo! – Michael Frederick Jun 26 '12 at 05:56
  • And I recommend this is the answer for this question .. not the above one .. its too complex.. this is very very easy to handle :) if some one want i can post the code, how to work with oriantation .. :) – Sameera Chathuranga Jun 26 '12 at 07:46
  • 6
    For anyone that's interested, I just added landscape support to the library. – Michael Frederick Jul 02 '12 at 19:18
  • I realized that your library performance is bad. My app performance decreased since I added your library. Can u realize? – Almas Adilbek Aug 07 '12 at 08:00
  • Yes, my app bad performance wad because of ur library. Now, I used ViewDeck library, performance now is amazing. You should make optimizations to your library. good luck! – Almas Adilbek Aug 07 '12 at 09:32
  • 1
    @AlmasAdilbek, can you explain what parts of your app experienced a decrease in performance? I would be happy to make any necessary optimizations. – Michael Frederick Aug 07 '12 at 17:54
  • I just used ur library. I succeeded with integration, build sidemenu, everything worked. When I started to build my centerView with UITableView, when I scroll table performance is not as it should be. It's not smooth. Just like android scrolling effect, not like an apple's smooth. – Almas Adilbek Aug 08 '12 at 02:11
  • Michael Frederick .....How to use this libary in Tabased Application in xcode 4.2... – sai Oct 23 '12 at 11:40
  • @AlmasAdilbek please open an issue and provide me with your code. If the problem is with the library I will definitely fix it. – Michael Frederick Oct 23 '12 at 16:01
  • @sai the sample project now includes an example of using MFSideMenu with a UITabBarController – Michael Frederick Oct 25 '12 at 19:15
  • @ Michael Frederick Ohh..kindly give me that URl – sai Oct 29 '12 at 08:25
  • Can you please add a demo for using it with iPad + Storyboard? – Adil Malik Jan 08 '14 at 16:12
  • This is the best library project for having side menus. Awesome work. But I just have one issue, I need to hide the status bar when the Menus are on and make it visible as I close the menu. Any help for this ? – JgdGuy Jul 15 '14 at 06:25
  • @MichaelFrederick Thanks for wonderful MFSideMenu lib, I have two doubts 1. Is it supporting storyboard ? 2. How to set center, left and right view controllers, If Side Menu need to add in after two - three view controllers in stack ? In my case I have registration screen before moving to Side Menu screen? Thanks in advance – Ajit Satarkar Jun 09 '16 at 11:32
34

I recently came across this, didn't actually look at the code or test the control, but looks like it may be a very decent starting point.

jtrevealsidebar

Edit: The reader should also take a look at the other answers :)

Zaky German
  • 14,324
  • 4
  • 25
  • 31
29

Check out MMDrawerController:

MMDrawerController

We couldn't find a library that we liked, so we just rolled our own.

NANNAV
  • 4,875
  • 4
  • 32
  • 50
kcharwood
  • 2,501
  • 19
  • 22
  • 10
    Just wanted to say that this is BY FAR the best implementation listed here. People new to this thread: use MMDrawerController. – mxcl Sep 11 '13 at 19:34
  • Appreciate the kind words! – kcharwood Sep 12 '13 at 19:45
  • I was using ViewDeck from past one year but I recently switched to MMDrawerController and I recommend everybody to use this. One of the best implementation in overall libraries. Thanks Kevin :) – Tariq Oct 08 '13 at 17:58
  • Checked this MMDrawerController and want to say that it's really the best option for side menu. Appreciated! Big thank you to @Michael Frederick – Resty May 13 '14 at 05:41
  • Does this support iOS8? Thanks – Bradley Thomas Dec 23 '14 at 18:45
12

The key idea that you have to set self.navigationController.view's frame or center. There is two event that you have to handle. (1) barButtonItem press. (2) pan gesture because of swiping.

You can send view controller to the background like this:

[self.view sendSubviewToBack:menuViewController.view];

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(buttonPressed:)];
    UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    [self.navigationController.view addGestureRecognizer:panGestureRecognizer];
    self.navigationItem.leftBarButtonItem = barButtonItem;
}

- (void)buttonPressed:(id)sender {

    CGRect destination = self.navigationController.view.frame;

    if (destination.origin.x > 0) {
        destination.origin.x = 0;
    } else {
        destination.origin.x = 320;
    }

    [UIView animateWithDuration:0.25 animations:^{
        self.navigationController.view.frame = destination;
    }];
}

- (void)handlePan:(UIPanGestureRecognizer *)recognizer
{
    static CGPoint originalCenter;

    if (recognizer.state == UIGestureRecognizerStateBegan)
    {
        originalCenter = recognizer.view.center;

    } else if (recognizer.state == UIGestureRecognizerStateChanged)
    {
        CGPoint translation = [recognizer translationInView:self.view];

        recognizer.view.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y);
    }
    else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled || recognizer.state == UIGestureRecognizerStateFailed)
    {
        if (recognizer.view.frame.origin.x < 160) {
            [UIView animateWithDuration:0.25 animations:^{
                recognizer.view.center = CGPointMake(384, 487.5);
            }];
        } else {
            [UIView animateWithDuration:0.25 animations:^{
                recognizer.view.center = CGPointMake(384 + 320, 487.5);
            }];
        }
    }
}
János
  • 32,867
  • 38
  • 193
  • 353
  • Works perfect and without too much code. You only need to specify the limits where the user can pan so your view – Moisés Olmedo Oct 25 '13 at 00:16
  • @Janos sir kindly help me am lacking in this for more days – Ragul Jun 22 '15 at 09:48
  • I have created the side out menu with the help of your above code, when slideout the back side shows only dark color not the view kindly help me – Ragul Jun 22 '15 at 10:04
  • Sir it works fine I use this above code only. the only thing is the background view dosn't shows I have upoted it please help me to show the background view – Ragul Jun 22 '15 at 10:09
  • @János and where to use this line of code [self.view sendSubviewToBack:menuViewController.view]; – Ragul Jun 22 '15 at 10:13
  • @János please help me – Ragul Jun 22 '15 at 10:55
  • have you checked with `Debug View Hierarchy`? – János Jun 22 '15 at 10:56
  • no i am new to iOS where to use this line of code [self.view sendSubviewToBack:menuViewController.view]; – Ragul Jun 22 '15 at 10:57
  • @János please help me where to use this line of code [self.view sendSubviewToBack:menuViewController.view]; – Ragul Jun 23 '15 at 04:38
  • I don't think that line is imortant at all, the concept is to set the frame of the container view, and handle the two event, pressing button, and panning – János Jun 23 '15 at 07:31
11

Even better is JASidePanels. Easily implemented and works for both iPhone and iPad.

Github link: JASidePanels

keaukraine
  • 5,315
  • 29
  • 54
user2330922
  • 141
  • 1
  • 6
11

Facebook's implementation places a UIPanGestureRecognizer on the UINavigationBar. Thus allowing to catch swipes where it's needed.

That allows to do things like, recognizing the touch direction in x/z directly as well as the speed they occurred with.

Also such kind of tinkering with UIViews (more than one on screen at a time with evidently different tasks -> thus different controllers) should (I'm tempted to say must) use iOS new ViewController-Containment features. Every implementation without that is simply bad as it tinkers with the view hierarchy in a way not intended by Apple.

On a sidenote: If you're really curious on how it can be done as close to Facebook's way as possible, check out the project I open sourced on Github PKRevealController .

NANNAV
  • 4,875
  • 4
  • 32
  • 50
pkluz
  • 4,871
  • 4
  • 26
  • 40
  • There is nothing wrong (or HIG non-compliant) with managing UIView-based custom UI. View controller containment came too late, so plenty of other approaches have been used. A related article: http://blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers/ – Jacob Jennings Dec 28 '11 at 20:57
  • 1
    Major developments won't be targeting a minimum version of iOS 5 for a long time, since a large number of users won't upgrade for a very long time. While many of them are 'hacks' as you say, it can certainly be done correctly. Staying within frameworks is definitely a safe bet, but sometimes a unique and custom UI requires more. – Jacob Jennings Dec 29 '11 at 15:22
8

There are tens of different libraries that developers have created to implement the Facebook/Path style navigation for iOS apps. I can list all of them but as you have asked for the best one, here are my two choices that i use to implement the side swipe navigation menu:

1) ECSlidingViewController Its very easy to implement and use Storyboards also. I haven't had any issues implementing it nor received any errors or anything like that. The link i provided has pretty much all the explanation in order to use it.

2) SWRevealViewController This library is easy to use and you can find a tutorial HERE, that shows how to implement it with all the explanation along with images. You can just follow the tutorial and thank me later.

AJ112
  • 5,291
  • 7
  • 45
  • 60
7

Another option is to use Scringo . It gives you a side-menu like in youtube/facebook apps, and also all kind of built-in features in the menu that you can choose to add (e.g chat, invite friends, etc...)

Adding the side-menu is simply by calling [ScringoAgent startSession...] and all configuration can be done on the site.

hungary54
  • 767
  • 8
  • 11
4

Refer to here, a very good starting point: slide out navigation like facebook and path

NANNAV
  • 4,875
  • 4
  • 32
  • 50
ZTAN
  • 71
  • 4
4

This question is quite popular but it all came down to easy importing and use for me... Here is what I use... SWRevealController.

I'm surprised people miss it... It really is GREAT!!! and easy to use. The developer even includes a few example projects that allow you to see how to use it in different scenarios.

jsetting32
  • 1,632
  • 2
  • 20
  • 45
3

A excellent guide to how develop your own Slide-Out-Navigation in Swift and Objective-C.

Objective-C

Swift

Victor Sigler
  • 23,243
  • 14
  • 88
  • 105
2

Both Gmail and Facebook make heavy use of web views, so it's hard to say what's native code and what's rendered HTML. However, looking at the interface, it looks like they've placed a UITableView with a width narrower than the screen width (320pt) underneath of a UIView that contains the content they want to display. Selecting different tableview rows probably swaps out a subview of the content view.

At least, that's how I'd approach the problem. It's hard to dictate what it should be. Just jump right in and start experimenting!

Ash Furrow
  • 12,391
  • 3
  • 57
  • 92
1

this is exactly the clone of the facebook sidebar : GHSidebarNav library

very easy to use. instruction are in

Max_Power89
  • 1,710
  • 1
  • 21
  • 38
1

the view in the right could be inside a subclass of UIScrollView. In this subclass you can override - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event to return YES only if the user touched the subview. This way you can place your transparent UIScrollView over any other view and pass through any touch event that takes place outside the subview; and you get scroll stuff for free.

For example:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    CGPoint location=[self convertPoint:point toView:subview];
    return (location.x > 0 && 
            location.x < subview.frame.size.width && 
            location.y > 0 && 
            location.y < subview.frame.size.height);
}

or:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    return [subview pointInside:
             [self convertPoint:point toView:subview] withEvent:event];
Alex Cio
  • 6,014
  • 5
  • 44
  • 74
javieralog
  • 1,337
  • 9
  • 10
1

Try adding your menu (that you swipe to get to) underneath the main view. Begin subscribing to touch events in the view.

Implement touchesMoved:, and check to see if the first gesture is vertical (scroll the main view, if needed), or horizontal (here you'd want to show the menu). If it's horizontal, begin invoking another method, - (void)scrollAwayMainView:(NSUInteger)pixels, whenever touchesMoved: gets called, calculating the number of pixels away from the starting point the main view should be, and passing that number into the method. In that methods implementation, run the following code:

[mainView scrollRectToVisible:CGRectMake:(pixels, 
                                          mainView.origin.y, 
                                          mainView.size.width, 
                                          mainView.size.height];
Alex Cio
  • 6,014
  • 5
  • 44
  • 74
aopsfan
  • 2,451
  • 19
  • 30
1

If you want I did this repo on github GSSlideMenu It allow you to create a Facebook-style menu BUT with a "back" webView (generally all repos that I've found has a tableView as the "back" view).

NANNAV
  • 4,875
  • 4
  • 32
  • 50
JAA
  • 1,024
  • 3
  • 20
  • 34
0

Check out my solution for this question:

How to create custom slide menu without third party library.?

One single class that you only need to subclass and fill in with content.

Here is the class. For more details, see the above link.

#import <UIKit/UIKit.h>

@interface IS_SlideMenu_View : UIView <UIGestureRecognizerDelegate>
{
    UIView* transparentBgView;
    BOOL hidden;
    int lastOrientation;
}

@property (strong, nonatomic) UIView *menuContainerV;

+ (id)sharedInstance;

- (BOOL)isShown;
- (void)hideSlideMenu;
- (void)showSlideMenu;

@end


#import "IS_SlideMenu_View.h"

@implementation IS_SlideMenu_View

+ (id)sharedInstance
{
    static id _sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [[[self class] alloc] init];
    });

    return _sharedInstance;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    frame = [[[UIApplication sharedApplication] delegate] window].frame;
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];

        transparentBgView = [[UIView alloc] initWithFrame:frame];
        [transparentBgView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.6]];
        [transparentBgView setAlpha:0];
        transparentBgView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gestureRecognized:)];
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(gestureRecognized:)];
        [transparentBgView addGestureRecognizer:tap];
        [transparentBgView addGestureRecognizer:pan];

        [self addSubview:transparentBgView];

        frame.size.width = 280;
        self.menuContainerV = [[UIView alloc] initWithFrame:frame];
        CALayer *l = self.menuContainerV.layer;
        l.shadowColor = [UIColor blackColor].CGColor;
        l.shadowOffset = CGSizeMake(10, 0);
        l.shadowOpacity = 1;
        l.masksToBounds = NO;
        l.shadowRadius = 10;
        self.menuContainerV.autoresizingMask = UIViewAutoresizingFlexibleHeight;

        [self addSubview: self.menuContainerV];
        hidden = YES;
    }

    //----- SETUP DEVICE ORIENTATION CHANGE NOTIFICATION -----
    UIDevice *device = [UIDevice currentDevice];
    [device beginGeneratingDeviceOrientationNotifications];
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:device];

    lastOrientation = [[UIDevice currentDevice] orientation];

    return self;
}

//********** ORIENTATION CHANGED **********
- (void)orientationChanged:(NSNotification *)note
{
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];    
    if(orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight){
        NSLog(@"%ld",orientation);
        if(!hidden && lastOrientation != orientation){
            [self hideSlideMenu];
            hidden = YES;
            lastOrientation = orientation;
        }
    }
}

- (void)showSlideMenu {
    UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
    self.frame = CGRectMake(0, 0, window.frame.size.width, window.frame.size.height);

    [self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-window.frame.size.width, 0)];

    [window addSubview:self];
//    [[UIApplication sharedApplication] setStatusBarHidden:YES];

    [UIView animateWithDuration:0.5 animations:^{
        [self.menuContainerV setTransform:CGAffineTransformIdentity];
        [transparentBgView setAlpha:1];
    } completion:^(BOOL finished) {
        NSLog(@"Show complete!");
        hidden = NO;
    }];
}

- (void)gestureRecognized:(UIGestureRecognizer *)recognizer
{
    if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        [self hideSlideMenu];
    } else if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
        static CGFloat startX;
        if (recognizer.state == UIGestureRecognizerStateBegan) {
            startX = [recognizer locationInView:self.window].x;
        } else
        if (recognizer.state == UIGestureRecognizerStateChanged) {
            CGFloat touchLocX = [recognizer locationInView:self.window].x;
            if (touchLocX < startX) {
                [self.menuContainerV setTransform:CGAffineTransformMakeTranslation(touchLocX - startX, 0)];
            }
        } else
        if (recognizer.state == UIGestureRecognizerStateEnded) {
            [self hideSlideMenu];
        }
    }
}

- (void)hideSlideMenu
{
    UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
    window.backgroundColor = [UIColor clearColor];
    [UIView animateWithDuration:0.5 animations:^{
        [self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-self.window.frame.size.width, 0)];
        [transparentBgView setAlpha:0];
    } completion:^(BOOL finished) {
        [self removeFromSuperview];
        [self.menuContainerV setTransform:CGAffineTransformIdentity];

//        [[UIApplication sharedApplication] setStatusBarHidden:NO];
        hidden = YES;
        NSLog(@"Hide complete!");
    }];
}

- (BOOL)isShown
{
    return !hidden;
}

@end
Community
  • 1
  • 1
dtarnok
  • 131
  • 1
  • 7