像Facebook的新iOS应用程序中那样,开发一种横扫菜单的最佳方法是什么?


155

似乎随着更多信息充斥于每个iPhone应用程序中,侧滑菜单正成为一种更为常见的界面元素。Facebook已将其包含在最新版本中,而新的Gmail应用程序似乎也包含了它。我想知道是否有人想到开发这样的东西的最有效方法,因为它正在变得越来越普遍。尽管我对如何构建它有自己的想法,但我很想听听其他人的想法。

Facebook滑动导航


我注意到,激活了横扫菜单后,您无法对视图的一部分进行任何操作。我的想法是在激活横扫功能后在图像中渲染当前视图并显示它的一部分
Denis

5
尼克·奥尼尔,您窥探了谁的Facebook个人资料?:-p
Constantino Tsarouhas


1
@Zoidberg我有兴趣了解更多有关此的信息。就个人而言,我喜欢sidenav!但是我是开发人员,而不是设计师。是什么让这个用户体验不好?
罗伯特·卡尔

3
请任何人观看“ 设计直观用户体验”,以了解苹果通过所谓的“汉堡菜单”识别出的问题
vikingosegundo 2014年

Answers:


34

我最近遇到了这个问题,实际上并没有查看代码或测试控件,但看起来它可能是一个非常不错的起点。

jtrevealsidebar

编辑:读者还应该看看其他答案:)


不允许用户以幻灯片
cannyboy

6
如果您正在寻找滑动功能。将手势识别器插入视图,然后将切换按钮替换为手势识别器。
CJ Teuben

但是,如果您想滑动并能够切换怎么办?
jsetting32

该产品已停产,所以我将转移到其他地方。
Mike Flynn 2014年

84

汤姆· 阿德里亚森(Tom Adriaenssen)为此提供了一个很棒的图书馆:Inferis / ViewDeck

它非常易于使用,并且拥有大量的追随者。

编辑:

有关更轻量的内容,请查看:commonmobile / MMDrawerController

它不具有ViewDeck的所有功能,但更易于修改和扩展。


6
我建议大家使用它。
阿尔马斯·阿迪贝克

3
最佳解决方案:灵活且易于使用
HotJard 2012年

2
我尝试了一下,并在示例中发现了许多错误。快速单击后,视图控制器未对齐
Torsten B

13
我们已经将其用于多个应用程序,但是很难对其进行维护和添加新功能。它也因尝试实施过多而遭受痛苦。我们决定编写自己的MMDrawerController。轻巧,专注:github.com/mutualmobile/MMDrawerController
kcharwood

1
MMDrawerController是一个很好的库。它还支持通用应用程序。iPhone和iPad应用程序
Erhan Demirci 2013年

48

我为此创建了一个库。它称为MFSideMenu

除其他功能外,它还包括对iphone + ipad,纵向+横向,左侧或右侧菜单,UITabBarController以及平移手势的支持。


它是一个很棒的项目..但是不支持横向模式..您能给我们一个提示吗,也可以在横向模式下使用它。.谢谢
Sameera Chathuranga 2012年

谢谢@SameeraChathuranga。如果有时间,我希望增加横向支持。唯一没有横向支持的部分是SideMenuViewController ...我相信您必须在这里做类似第二个答案的事情:stackoverflow.com/questions/2508630/…。随意分叉并为回购做贡献!
Michael Frederick

我建议这是这个问题的答案..而不是上面的问题..它太复杂了..这非常容易处理:)如果有人希望我可以发布代码,那么如何使用格式..: )
Sameera Chathuranga 2012年

6
对于感兴趣的任何人,我都向库中添加了景观支持。
Michael Frederick

1
@AlmasAdilbek,您能解释一下应用程序的哪些部分性能下降吗?我很乐意进行任何必要的优化。
迈克尔·弗雷德里克

29

查看MMD​​rawerController:

MMDrawerController

我们找不到喜欢的库,因此我们自己推出了自己的库。


10
只是想说这是BY FAR此处列出的最佳实现。刚接触此线程的人员:使用MMDrawerController。
mxcl 2013年

欣赏好客的话!
kcharwood 2013年

过去一年来我一直在使用ViewDeck,但最近我切换到了MMDrawerController,我建议每个人都使用它。整体库中最好的实现之一。谢谢凯文:)
Tariq

检查了此MMDrawerController并想说它确实是侧面菜单的最佳选择。感激!非常感谢@Michael Frederick
Resty 2014年

这是否支持iOS8?谢谢
布拉德·托马斯

12

您必须设置self.navigationController.view的frame或center关键思想。有两个事件,你必须处理。(1)按barButtonItem。(2)因为刷卡而摇动手势

您可以像这样将视图控制器发送到后台:

[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);
            }];
        }
    }
}

完美的作品,没有太多的代码。你只需要指定范围,用户可以移动,这样您的视图
莫伊塞斯奥尔梅多

@Janos先生,请您再多几天来帮助我
Ragul

我已经在您的上述代码的帮助下创建了sideout菜单,当滑出后的背面仅显示深色时,该视图对我没有帮助
Ragul 2015年

主席先生,它工作正常,我只使用上面的代码。唯一的事情是背景视图没有显示我赞成,请帮助我显示背景视图
Ragul 2015年

@János以及在哪里使用此行代码[self.view sendSubviewToBack:menuViewController.view];
Ragul


11

Facebook的实现将UIPanGestureRecognizer放在UINavigationBar上。因此可以在需要的地方捕捉滑动。

这样就可以进行诸如以x / z直接识别触摸方向及其发生速度的操作。

同样,这种对UIView的修改(一次在屏幕上显示多个任务,显然任务不同->因此控制器也不同)应该(我很想说必须)使用iOS新的ViewController-Containment功能。没有它的每个实现都是很糟糕的,因为它以苹果不想要的方式修改视图层次结构。

旁注:如果您真的对如何尽可能接近Facebook的方式感到好奇,请查看我在Github PKRevealController上开源的项目。


管理基于UIView的自定义UI没错(或与HIG不兼容)。View Controller遏制来不及了,因此已经使用了许多其他方法。相关文章:blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers
Jacob Jennings

1
长期以来,主要的开发目标都不会针对最低版本的iOS 5,因为大量用户不会在很长时间内升级。正如您所说的,其中许多都是“ hacks”,当然可以正确地做到这一点。停留在框架内绝对是一个安全的选择,但是有时唯一的和自定义的UI需要更多。
Jacob Jennings

8

开发人员创建了数十种不同的库来为iOS应用实现Facebook / Path样式导航。我可以列出所有这些,但是正如您要求的最好的一样,这是我用来实现侧向滑动导航菜单的两个选择:

1)ECSlidingViewController 它也非常容易实现和使用Storyboard。我没有实现它的任何问题,也没有收到任何错误或类似的东西。我提供的链接具有使用它的几乎所有解释。

2)SWRevealViewController 该库易于使用,您可以在此处找到教程,其中显示了如何实现所有说明以及图像。您可以按照本教程进行操作,稍后再谢谢。


1
SWRevealViewController是最简单的一种,用它没有错误做工不错
维沙尔dharankar

7

另一种选择是使用Scringo。它为您提供了youtube / facebook应用程序中的侧菜单,并且还提供了菜单中您可以选择添加的所有内置功能(例如,聊天,邀请朋友等)。

只需调用[ScringoAgent startSession ...],即可添加侧面菜单,所有配置均可在站点上完成。



4

这个问题很受欢迎,但是全部归结为易于导入并为我使用...这是我使用的... SWRevealController

我很惊讶人们错过了它……真的很棒!!!且易于使用。开发人员甚至包括一些示例项目,使您可以查看如何在不同的情况下使用它。



2

Gmail和Facebook都大量使用Web视图,因此很难说出什么是本机代码,什么是呈现的HTML。但是,看一下界面,看起来他们放置了一个UITableView,其宽度比包含要显示内容的UIView的屏幕宽度(320pt)窄。选择不同的表视图行可能会换出内容视图的子视图。

至少,这就是我解决问题的方式。很难决定应该是什么。只要跳进去开始实验!


嘿,谁能告诉我如何在Android中创建Facebook类型的滑动视图和菜单面板
Neerav Shah 2013年

1

如果您想在github上执行仓库GSSlideMenu, 它允许您使用“后” webView创建Facebook风格的菜单BUT(通常,我发现的所有仓库都具有tableView作为“后”视图)。



1

右边的视图可能在UIScrollView的子类内。在此子类中,您可以重写- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event以仅在用户触摸子视图时返回YES。这样,您可以将透明的UIScrollView放置在任何其他视图之上,并通过子视图外部发生的任何触摸事件。您可以免费获得滚动内容。

例如:

- (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);
}

要么:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    return [subview pointInside:
             [self convertPoint:point toView:subview] withEvent:event];

1

尝试在主视图下添加菜单(滑动即可进入)。开始订阅以触摸视图中的事件。

实施touchesMoved:,并检查第一个gesture是垂直的(如果需要,滚动主视图)还是水平的(这里要显示菜单)。如果它是水平的,则开始调用另一个方法,- (void)scrollAwayMainView:(NSUInteger)pixels每当touchesMoved:被调用时,计算离开主视图应起点的像素数,然后将该数字传递给该方法。在该方法的实现中,运行以下代码:

[mainView scrollRectToVisible:CGRectMake:(pixels, 
                                          mainView.origin.y, 
                                          mainView.size.width, 
                                          mainView.size.height];

0

查看我针对此问题的解决方案:

如何在没有第三方库的情况下创建自定义幻灯片菜单?

您只需要对其中的一个子类进行子类化并填写内容即可。

这是课程。有关更多详细信息,请参见上面的链接。

#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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.