Quelle est la meilleure façon de développer un menu à balayage latéral comme celui de la nouvelle application iOS de Facebook?

155

Il semble que les menus à balayage latéral deviennent un élément d'interface de plus en plus courant à mesure que de plus en plus d'informations sont entassées dans chaque application iPhone. Facebook l'a inclus dans sa dernière version et la nouvelle application Gmail semble l'inclure également . Je me demandais si quelqu'un avait des idées sur le moyen le plus efficace de développer quelque chose comme ça, car il devient un élément d'interface plus courant. Bien que j'aie mes propres idées sur la façon de construire cela, je suis curieux d'entendre ce que les autres pensent.

Navigation par balayage sur Facebook

Nick ONeill
la source
J'ai remarqué que lorsque le menu de balayage latéral est activé, vous ne pouvez rien faire avec une partie balayée de la vue. Mon idée était de rendre la vue actuelle dans l'image et d'en afficher une partie, lorsque le balayage latéral est activé
Denis
5
Dans quel profil Facebook avez-vous jeté un œil, Nick O'Neill? :-p
Constantino Tsarouhas
1
duplication possible de SplitView mais sur iPhone
JosephH
1
@Zoidberg Je suis intéressé d'en savoir plus à ce sujet. Personnellement, j'aime le sidenav! Mais je suis un développeur, pas un designer. Qu'est-ce qui rend cet UX pauvre?
Robert Karl
3
S'il vous plaît, tout le monde regarde Concevoir des expériences utilisateur intuitives pour en savoir plus sur les problèmes identifiés par Apple avec ce soi-disant "menus hamburgers"
vikingosegundo

Réponses:

34

Je suis récemment tombé sur cela, je n'ai pas vraiment regardé le code ou testé le contrôle, mais il semble que cela puisse être un point de départ très décent.

jtrevealsidebar

Edit: Le lecteur devrait également jeter un coup d'œil aux autres réponses :)

Zaky allemand
la source
Ne permet pas à l'utilisateur de glisser
cannyboy
6
Si vous recherchez une fonctionnalité de balayage. Insérez un outil de reconnaissance de gestes dans la vue, puis remplacez le bouton bascule par la reconnaissance de gestes.
CJ Teuben
Mais que se passe-t-il si vous voulez faire glisser et pouvoir basculer?
jsetting32
Ceci est interrompu donc je déménagerais ailleurs.
Mike Flynn le
84

Il existe une excellente bibliothèque pour cela par Tom Adriaenssen: Inferis / ViewDeck

Il est très facile à utiliser et a un public assez large.

ÉDITER:

Pour quelque chose d'un peu plus léger, consultez: mutualmobile / MMDrawerController

Il n'a pas toutes les fonctionnalités de ViewDeck mais est plus simple à modifier et à étendre.

Chris Knadler
la source
6
Je recommande à tout le monde de l'utiliser.
Almas Adilbek
3
meilleure solution: flexible et simple à utiliser
HotJard
2
Je l'ai essayé et j'ai trouvé beaucoup de bogues dans leur exemple. Après quelques clics rapides, les contrôleurs de vue sont mal alignés
Torsten B
13
Nous l'avons utilisé pour plusieurs applications, mais il était très difficile de le maintenir et d'y ajouter de nouvelles fonctionnalités. Il a également souffert d'essayer de trop mettre en œuvre. Nous avons décidé d'écrire le nôtre appelé MMDrawerController. Léger et concentré: github.com/mutualmobile/MMDrawerController
kcharwood
1
MMDrawerController est une très bonne bibliothèque. Il prend également en charge l'application universelle. applications iphone et ipad
Erhan Demirci
48

J'ai créé une bibliothèque pour cela. Il s'appelle MFSideMenu .

Entre autres choses, il comprend la prise en charge de l'iphone + ipad, portrait + paysage, menu sur le côté gauche ou droit, UITabBarController et les gestes de panoramique.

Michael Frederick
la source
C'est un excellent projet .. mais il ne prend pas en charge le mode paysage .. Pouvez-vous nous donner un indice pour l'utiliser aussi en mode paysage .. Merci
Sameera Chathuranga
Merci @SameeraChathuranga. J'espère ajouter le support du paysage si j'ai un peu de temps. La seule partie qui ne prend pas en charge le paysage est le SideMenuViewController ... Je crois que vous devriez faire quelque chose comme la deuxième réponse ici: stackoverflow.com/questions/2508630/… . N'hésitez pas à fork et à contribuer au repo!
Michael Frederick
Et je recommande ceci est la réponse à cette question .. pas celle ci-dessus .. c'est trop complexe .. c'est très très facile à gérer :) si quelqu'un veut je peux poster le code, comment travailler avec oriantation ..: )
Sameera Chathuranga
6
Pour tous ceux qui sont intéressés, je viens d'ajouter la prise en charge du paysage à la bibliothèque.
Michael Frederick
1
@AlmasAdilbek, pouvez-vous expliquer quelles parties de votre application ont connu une baisse de performances? Je serais heureux de faire les optimisations nécessaires.
Michael Frederick
29

Découvrez MMDrawerController:

MMDrawerController

Nous n'avons pas pu trouver une bibliothèque qui nous plaisait, alors nous avons simplement créé la nôtre.

Kcharwood
la source
10
Je voulais juste dire que c'est de loin la meilleure implémentation répertoriée ici. Les nouveaux utilisateurs de ce fil: utilisez MMDrawerController.
mxcl le
Appréciez les mots gentils!
kcharwood
J'utilisais ViewDeck depuis un an mais je suis récemment passé à MMDrawerController et je recommande à tout le monde de l'utiliser. Une des meilleures implémentations dans les bibliothèques globales. Merci Kevin :)
Tariq
Vérifié ce MMDrawerController et je veux dire que c'est vraiment la meilleure option pour le menu latéral. Apprécié! Un grand merci à @Michael Frederick
Resty
Est-ce que cela prend en charge iOS8? Merci
Brad Thomas
12

L' idée clé que vous devez définir le cadre ou le centre de self.navigationController.view . Il y a deux événements que vous devez gérer. (1) barButtonItem appuyez sur . (2) geste panoramique à cause du glissement.

Vous pouvez envoyer le contrôleur de vue en arrière-plan comme ceci:

[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
la source
Fonctionne parfaitement et sans trop de code. Il vous suffit de spécifier les limites auxquelles l'utilisateur peut faire un panoramique pour votre vue
Moisés Olmedo
@Janos monsieur, aidez-moi gentiment que je manque cela depuis plus de jours
Ragul
J'ai créé le menu latéral avec l'aide de votre code ci-dessus, lorsque le glissement du verso ne montre que la couleur sombre, pas la vue, aidez-moi gentiment
Ragul
Monsieur, cela fonctionne bien, j'utilise uniquement ce code ci-dessus. la seule chose est que la vue d'arrière-plan ne montre pas que je l'ai mise à jour s'il vous plaît aidez-moi à afficher la vue d'arrière-plan
Ragul
@ János et où utiliser cette ligne de code [self.view sendSubviewToBack: menuViewController.view];
Ragul
11

Encore mieux, JASidePanels . Facilement implémenté et fonctionne pour iPhone et iPad.

Lien Github: JASidePanels

utilisateur2330922
la source
11

La mise en œuvre de Facebook place un UIPanGestureRecognizer sur UINavigationBar. Permettant ainsi de capturer des balayages là où c'est nécessaire.

Cela permet de faire des choses comme, reconnaître directement la direction du toucher en x / z ainsi que la vitesse à laquelle ils se sont produits.

Un tel type de bricolage avec UIViews (plus d'un à l'écran à la fois avec des tâches évidemment différentes -> donc différents contrôleurs) devrait (je suis tenté de dire qu'il faut) utiliser les nouvelles fonctionnalités de ViewController-Containment d'iOS. Toute implémentation sans cela est tout simplement mauvaise car elle bricole la hiérarchie des vues d'une manière non prévue par Apple.

En passant: si vous êtes vraiment curieux de savoir comment cela peut être fait aussi près que possible de Facebook, consultez le projet que j'ai ouvert sur Github PKRevealController .

pkluz
la source
Il n'y a rien de mal (ou non conforme HIG) à la gestion de l'interface utilisateur personnalisée basée sur UIView. Le confinement du contrôleur de vue est arrivé trop tard, de nombreuses autres approches ont donc été utilisées. Un article connexe: blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers
Jacob Jennings
1
Les développements majeurs ne cibleront pas une version minimale d'iOS 5 pendant longtemps, car un grand nombre d'utilisateurs ne mettront pas à niveau pendant très longtemps. Bien que beaucoup d'entre eux soient des «hacks» comme vous le dites, cela peut certainement être fait correctement. Rester dans les cadres est certainement une valeur sûre, mais parfois une interface utilisateur unique et personnalisée en nécessite plus.
Jacob Jennings
8

Il existe des dizaines de bibliothèques différentes que les développeurs ont créées pour implémenter la navigation de style Facebook / Path pour les applications iOS. Je peux tous les énumérer, mais comme vous avez demandé le meilleur, voici mes deux choix que j'utilise pour implémenter le menu de navigation par balayage latéral:

1) ECSlidingViewController Son très facile à mettre en œuvre et à utiliser également des storyboards. Je n'ai eu aucun problème de mise en œuvre ni reçu d'erreurs ou quoi que ce soit de ce genre. Le lien que j'ai fourni a à peu près toutes les explications pour l'utiliser.

2) SWRevealViewController Cette bibliothèque est facile à utiliser et vous pouvez trouver un tutoriel ICI , qui montre comment l'implémenter avec toutes les explications avec des images. Vous pouvez simplement suivre le tutoriel et me remercier plus tard.

AJ112
la source
1
SWRevealViewController est le plus simple, utilisé il aucun bogue fonctionne bien
vishal dharankar
7

Une autre option est d'utiliser Scringo . Il vous donne un menu latéral comme dans les applications youtube / facebook, ainsi que toutes sortes de fonctionnalités intégrées dans le menu que vous pouvez choisir d'ajouter (par exemple, chat, inviter des amis, etc.)

L'ajout du menu latéral se fait simplement en appelant [ScringoAgent startSession ...] et toute la configuration peut être effectuée sur le site.

Hongrie54
la source
4

Cette question est assez populaire mais tout se résume à une importation et une utilisation faciles pour moi ... Voici ce que j'utilise ... SWRevealController .

Je suis surpris que les gens le manquent ... C'est vraiment génial !!! et facile à utiliser. Le développeur inclut même quelques exemples de projets qui vous permettent de voir comment l'utiliser dans différents scénarios.

jsetting32
la source
2

Gmail et Facebook font un usage intensif des vues Web, il est donc difficile de dire quel est le code natif et ce qui est rendu HTML. Cependant, en regardant l'interface, il semble qu'ils aient placé un UITableView avec une largeur plus étroite que la largeur de l'écran (320pt) sous un UIView qui contient le contenu qu'ils souhaitent afficher. La sélection de différentes lignes de vue table remplace probablement une sous-vue de la vue de contenu.

Du moins, c'est ainsi que j'aborderais le problème. Il est difficile de dicter ce que cela devrait être. Lancez-vous et commencez à expérimenter!

Sillon de cendre
la source
hey quelqu'un peut-il me dire comment créer la vue de balayage de type facebook et le panneau de menu dans Android
Neerav Shah
1

Si vous voulez, j'ai fait ce repo sur github GSSlideMenu Il vous permet de créer un menu de style Facebook MAIS avec un webView «arrière» (généralement tous les dépôts que j'ai trouvés ont une vue table comme vue «arrière»).

JAA
la source
1

la vue de droite pourrait être à l'intérieur d'une sous-classe de UIScrollView. Dans cette sous-classe, vous pouvez remplacer - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)eventpour renvoyer OUI uniquement si l'utilisateur a touché la sous-vue. De cette façon, vous pouvez placer votre UIScrollView transparent sur n'importe quelle autre vue et passer par tout événement tactile qui a lieu en dehors de la sous-vue; et vous obtenez des trucs de défilement gratuitement.

Par exemple:

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

ou:

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

Essayez d'ajouter votre menu (auquel vous faites glisser votre doigt pour y accéder) sous la vue principale. Commencez à vous abonner pour toucher les événements dans la vue.

Implémentez touchesMoved:et vérifiez si le premier gestureest vertical (faites défiler la vue principale, si nécessaire) ou horizontal (ici, vous voudrez afficher le menu). Si elle est horizontale, commencez à invoquer une autre méthode, - (void)scrollAwayMainView:(NSUInteger)pixelschaque fois qu'elle touchesMoved:est appelée, calculez le nombre de pixels éloignés du point de départ de la vue principale et transmettez ce nombre à la méthode. Dans cette implémentation de méthodes, exécutez le code suivant:

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

Découvrez ma solution pour cette question:

Comment créer un menu de diapositives personnalisé sans bibliothèque tierce.?

Une seule classe qu'il vous suffit de sous-classer et de remplir avec du contenu.

Voici la classe. Pour plus de détails, consultez le lien ci-dessus.

#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
David Tarnok
la source