¿Cuál es la mejor manera de desarrollar un menú lateral como el de la nueva aplicación iOS de Facebook?


155

Parece que los menús de desplazamiento lateral se están convirtiendo en un elemento de interfaz más común a medida que se acumula más información en cada aplicación de iPhone. Facebook lo ha incluido en su última versión y la nueva aplicación de Gmail parece incluirlo también . Me preguntaba si alguien tenía ideas sobre la forma más eficiente de desarrollar algo como esto, ya que se está convirtiendo en un elemento de interfaz más común. Si bien tengo mis propios pensamientos sobre cómo construir esto, tengo curiosidad por escuchar lo que piensan otras personas.

Navegación de deslizamiento de Facebook


Me di cuenta de que cuando se activa el menú de desplazamiento lateral, no se puede hacer nada con la parte deslizada de la vista. Mi idea era renderizar la vista actual en la imagen y mostrar parte de ella, cuando se activa el desplazamiento lateral
Denis

55
¿En qué perfil de Facebook echaste un vistazo, Nick O'Neill? :-p
Constantino Tsarouhas

1
posible duplicado de SplitView pero en iPhone
JosephH

1
@Zoidberg Estoy interesado en saber más sobre esto. Personalmente, me gusta el sidenav! Pero soy un desarrollador, no un diseñador. ¿Qué hace que este UX sea pobre?
Robert Karl

3
Por favor, mire a Designing Intuitive User Experiences para conocer qué problemas ha identificado Apple con estos llamados "menús de hamburguesas"
vikingosegundo

Respuestas:


34

Recientemente me encontré con esto, en realidad no miré el código ni probé el control, pero parece que puede ser un punto de partida muy decente.

jtrevealsidebar

Editar: el lector también debe echar un vistazo a las otras respuestas :)


No permite que el usuario se deslice
cannyboy

66
Si buscas funcionalidad de deslizamiento. Inserte un reconocedor de gestos en la vista y luego reemplace el botón de alternar con el reconocedor de gestos.
CJ Teuben

Pero, ¿qué pasa si quieres deslizar y poder alternar?
jsetting32

Esto se descontinúa, así que me mudaría a otro lado.
Mike Flynn

84

Hay una gran biblioteca para esto de Tom Adriaenssen: Inferis / ViewDeck

Es muy fácil de usar y tiene muchos seguidores.

EDITAR:

Para algo un poco más liviano, consulte: mutualmobile / MMDrawerController

No tiene todas las características de ViewDeck, pero es más fácil de modificar y ampliar.


66
Recomiendo a todos que usen esto.
Almas Adilbek

3
mejor solución: flexible y fácil de usar
HotJard

2
Lo probé y encontré muchos errores en su ejemplo. Después de algunos clics rápidos, los controladores de vista están desalineados
Torsten B

13
Lo hemos usado para varias aplicaciones, pero fue muy difícil mantenerlo y agregarle nuevas funciones. También ha sufrido por tratar de implementar demasiado. Decidimos escribir nuestro propio MMDrawerController. Ligero y enfocado: github.com/mutualmobile/MMDrawerController
kcharwood

1
MMDrawerController es una muy buena biblioteca. También es compatible con la aplicación universal. aplicaciones para iphone y ipad
Erhan Demirci

48

Creé una biblioteca para esto. Se llama MFSideMenu .

Entre otras cosas, incluye soporte para iphone + ipad, vertical + horizontal, menú en el lado izquierdo o derecho, UITabBarController y gestos panorámicos.


Es un gran proyecto ... pero no es compatible con el modo horizontal ... ¿Puede darnos una pista para usarlo también en modo horizontal? Gracias
Sameera Chathuranga

Gracias @SameeraChathuranga. Espero agregar soporte de paisaje si tengo algo de tiempo. La única parte que no tiene soporte horizontal es el SideMenuViewController ... Creo que tendría que hacer algo como la segunda respuesta aquí: stackoverflow.com/questions/2508630/… . ¡No dude en bifurcar y contribuir al repositorio!
Michael Frederick

Y recomiendo que esta sea la respuesta a esta pregunta ... no la anterior ... es demasiado compleja ... esto es muy fácil de manejar :) si alguien quiere puedo publicar el código, cómo trabajar con la orientación ...: )
Sameera Chathuranga

66
Para cualquiera que esté interesado, acabo de agregar soporte horizontal a la biblioteca.
Michael Frederick

1
@AlmasAdilbek, ¿puede explicar qué partes de su aplicación experimentaron una disminución en el rendimiento? Estaré encantado de hacer las optimizaciones necesarias.
Michael Frederick

29

Echa un vistazo a MMDrawerController:

MMDrawerController

No pudimos encontrar una biblioteca que nos gustara, así que simplemente creamos la nuestra.


10
Solo quería decir que esta es, con diferencia, la mejor implementación que se enumera aquí. Gente nueva en este hilo: use MMDrawerController.
mxcl

Agradezco las amables palabras!
kcharwood

Estaba usando ViewDeck desde hace un año, pero recientemente cambié a MMDrawerController y recomiendo a todos que lo usen. Una de las mejores implementaciones en bibliotecas en general. Gracias Kevin :)
Tariq

Revisé este MMDrawerController y quiero decir que es realmente la mejor opción para el menú lateral. ¡Apreciado! Muchas gracias a @Michael Frederick
Resty

¿Esto es compatible con iOS8? Gracias
Brad Thomas

12

La idea clave de que tiene que establecer el marco o centro de self.navigationController.view . Hay dos eventos que debes manejar. (1) barButtonItem press . (2) gesto de desplazamiento debido al deslizamiento.

Puede enviar el controlador de vista al fondo de esta manera:

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

Funciona perfecto y sin demasiado código. Solo necesita especificar los límites donde el usuario puede desplazarse para que su vista
Moisés Olmedo

@Janos señor amablemente ayúdenme a que me falte esto por más días
Ragul

He creado el menú lateral con la ayuda de su código anterior, cuando el deslizador de la parte posterior muestra solo el color oscuro, no la vista amablemente me ayuda
Ragul

Señor, funciona bien. Solo uso este código anterior. lo único es que la vista de fondo no se muestra. Lo he subido. Ayúdame a mostrar la vista de fondo
Ragul

@ János y dónde usar esta línea de código [self.view sendSubviewToBack: menuViewController.view];
Ragul


11

La implementación de Facebook coloca un UIPanGestureRecognizer en el UINavigationBar. Por lo tanto, permite atrapar golpes donde sea necesario.

Eso permite hacer cosas como reconocer la dirección táctil en x / z directamente, así como la velocidad con la que ocurrieron.

Además, este tipo de retoques con UIViews (más de uno en la pantalla a la vez con tareas evidentemente diferentes -> por lo tanto, diferentes controladores) deberían (estoy tentado a decir que deben) usar las nuevas características ViewController-Containment de iOS. Cada implementación sin eso es simplemente mala, ya que juega con la jerarquía de vistas de una manera no prevista por Apple.

En una nota al margen: si tiene curiosidad sobre cómo se puede hacer lo más cerca posible de Facebook, consulte el proyecto que abrí en Github PKRevealController .


No hay nada malo (o HIG no compatible) con la gestión de la IU personalizada basada en UIView. La contención del controlador de vista llegó demasiado tarde, por lo que se han utilizado muchos otros enfoques. Un artículo relacionado: blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers
Jacob Jennings

1
Los desarrollos importantes no se centrarán en una versión mínima de iOS 5 durante mucho tiempo, ya que una gran cantidad de usuarios no se actualizarán durante mucho tiempo. Si bien muchos de ellos son 'hacks' como usted dice, ciertamente se puede hacer correctamente. Mantenerse dentro de los marcos es definitivamente una apuesta segura, pero a veces una interfaz de usuario única y personalizada requiere más.
Jacob Jennings

8

Hay decenas de bibliotecas diferentes que los desarrolladores han creado para implementar la navegación de estilo Facebook / Path para aplicaciones iOS. Puedo enumerarlos todos, pero como usted ha pedido el mejor, aquí están mis dos opciones que uso para implementar el menú de navegación de deslizamiento lateral:

1) ECSlidingViewController Es muy fácil de implementar y usar Storyboards también. No tuve problemas para implementarlo ni recibí ningún error ni nada por el estilo. El enlace que proporcioné tiene casi toda la explicación para poder usarlo.

2) SWRevealViewController Esta biblioteca es fácil de usar y puede encontrar un tutorial AQUÍ , que muestra cómo implementarlo con toda la explicación junto con las imágenes. Puedes seguir el tutorial y agradecerme más tarde.


1
SWRevealViewController es el más simple, no se utiliza ningún error funciona bien
vishal dharankar

7

Otra opción es usar Scringo . Le ofrece un menú lateral como en las aplicaciones de youtube / facebook, y también todo tipo de funciones integradas en el menú que puede elegir agregar (por ejemplo, chat, invitar amigos, etc.)

Agregar el menú lateral es simplemente llamando a [ScringoAgent startSession ...] y toda la configuración se puede hacer en el sitio.



4

Esta pregunta es bastante popular, pero todo se redujo a una fácil importación y uso para mí ... Esto es lo que uso ... SWRevealController .

Me sorprende que la gente se lo pierda ... ¡Realmente es GENIAL! y fácil de usar El desarrollador incluso incluye algunos proyectos de ejemplo que le permiten ver cómo usarlo en diferentes escenarios.



2

Tanto Gmail como Facebook hacen un uso intensivo de las vistas web, por lo que es difícil decir qué es el código nativo y qué se representa en HTML. Sin embargo, al mirar la interfaz, parece que han colocado un UITableView con un ancho más estrecho que el ancho de la pantalla (320pt) debajo de una UIView que contiene el contenido que desean mostrar. La selección de diferentes filas de vistas de tabla probablemente intercambia una subvista de la vista de contenido.

Al menos, así es como abordaría el problema. Es difícil dictar lo que debería ser. ¡Simplemente salta y comienza a experimentar!


Hola, ¿alguien puede decirme cómo crear la vista de deslizamiento de tipo Facebook y el panel de menú en Android
Neerav Shah

1

Si lo desea, hice este repositorio en github GSSlideMenu Le permite crear un menú al estilo de Facebook PERO con una vista web "posterior" (generalmente todos los repositorios que he encontrado tienen una vista de tabla como la vista "posterior").



1

la vista de la derecha podría estar dentro de una subclase de UIScrollView. En esta subclase puedes anular- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event para devolver SÍ solo si el usuario tocó la subvista. De esta manera, puede colocar su UIScrollView transparente sobre cualquier otra vista y pasar por cualquier evento táctil que tenga lugar fuera de la subvista; y obtienes material de desplazamiento gratis.

Por ejemplo:

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

o:

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

1

Intenta agregar tu menú (que deslizas para acceder) debajo de la vista principal. Comience a suscribirse a eventos táctiles en la vista.

Implemente touchesMoved:y verifique si el primero gesturees vertical (desplazar la vista principal, si es necesario) u horizontal (aquí desea mostrar el menú). Si es horizontal, comience a invocar otro método, - (void)scrollAwayMainView:(NSUInteger)pixelscada vez que touchesMoved:se llame, calculando el número de píxeles lejos del punto de inicio que debería ser la vista principal y pasando ese número al método. En esa implementación de métodos, ejecute el siguiente código:

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

0

Mira mi solución para esta pregunta:

¿Cómo crear un menú de diapositivas personalizado sin una biblioteca de terceros?

Una sola clase que solo necesita subclasificar y completar con contenido.

Aquí está la clase. Para más detalles, vea el enlace de arriba.

#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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.