Qual è il modo migliore per sviluppare un menu a scorrimento laterale come quello nella nuova app iOS di Facebook?


155

Sembra che i menu a scorrimento laterale stiano diventando un elemento di interfaccia più comune quando più informazioni vengono stipate in ogni app per iPhone. Facebook l'ha inclusa nell'ultima versione e anche la nuova app Gmail sembra includerla . Mi chiedevo se qualcuno avesse pensato al modo più efficiente di sviluppare qualcosa del genere in quanto sta diventando un elemento di interfaccia più comune. Mentre ho i miei pensieri su come costruirlo, sono curioso di sentire cosa pensano gli altri.

Navigazione su Facebook


Ho notato che quando il menu di scorrimento laterale è attivato, non è possibile eseguire alcuna operazione con la parte di visualizzazione trascinata. La mia idea era di rendere la vista corrente nell'immagine e di visualizzarne una parte, quando si attiva lo scorrimento laterale
Denis,

5
Di quale profilo Facebook hai fatto capolino, Nick O'Neill? :-p
Constantino Tsarouhas,

1
possibile duplicato di SplitView ma su iPhone
JosephH

1
@Zoidberg Sono interessato a saperne di più su questo. Personalmente, mi piace il sidenav! Ma sono uno sviluppatore, non un designer. Cosa rende questa UX povera?
Robert Karl,

3
Per favore, chiunque guardi Progettare esperienze utente intuitive per conoscere quali problemi ha identificato Apple con questi cosiddetti "menu di hamburger"
vikingosegundo,

Risposte:


34

Di recente mi sono imbattuto in questo, in realtà non ho guardato il codice o testato il controllo, ma sembra che possa essere un punto di partenza molto decente.

jtrevealsidebar

Modifica: il lettore dovrebbe anche dare un'occhiata alle altre risposte :)


Non consente all'utente di scorrere
cannyboy

6
Se stai cercando la funzionalità di scorrimento. Inserisci un riconoscimento gesti nella vista, quindi sostituisci il pulsante di attivazione / disattivazione con il riconoscimento gesti.
CJ Teuben,

Ma cosa succede se si desidera scorrere e essere in grado di attivare / disattivare?
jsetting32

Questo è sospeso quindi mi sposterò altrove.
Mike Flynn,

84

C'è una grande biblioteca per questo di Tom Adriaenssen: Inferis / ViewDeck

È molto facile da usare e ha un seguito abbastanza ampio.

MODIFICARE:

Per qualcosa di un po 'più leggero, controlla: mutualmobile / MMDrawerController

Non ha tutte le funzionalità di ViewDeck ma è più semplice da modificare ed estendere.


6
Consiglio a tutti di usarlo.
Almas Adilbek,

3
migliore soluzione: flessibile e semplice da usare
HotJard

2
L'ho provato e ho trovato molti bug nel loro esempio. Dopo alcuni clic rapidi, i visualizzatori non sono allineati
Torsten B,

13
L'abbiamo usato per diverse app, ma è stato molto difficile da mantenere e aggiungere nuove funzionalità. Ha anche sofferto nel tentativo di implementare troppo. Abbiamo deciso di scrivere il nostro chiamato MMDrawerController. Leggero e focalizzato: github.com/mutualmobile/MMDrawerController
kcharwood

1
MMDrawerController è un'ottima libreria. Inoltre supporta l'app universale. app per iPhone e iPad
Erhan Demirci,

48

Ho creato una libreria per questo. Si chiama MFSideMenu .

Tra le altre cose include il supporto per iphone + ipad, verticale + orizzontale, menu sul lato sinistro o destro, UITabBarController e gesti di panoramica.


È un grande progetto .. ma non supporta la modalità orizzontale .. Puoi darci un suggerimento per usarlo anche in modalità orizzontale .. Grazie
Sameera Chathuranga,

Grazie @SameeraChathuranga. Spero di aggiungere il supporto per il paesaggio se avrò del tempo. L'unica parte che non ha il supporto orizzontale è il SideMenuViewController ... Credo che dovresti fare qualcosa come la seconda risposta qui: stackoverflow.com/questions/2508630/… . Sentiti libero di sborsare e contribuire al repo!
Michael Frederick,

E raccomando questa è la risposta a questa domanda .. non quella sopra .. è troppo complessa .. è molto molto facile da gestire :) se qualcuno vuole posso pubblicare il codice, come lavorare con l'oriantation ..: )
Sameera Chathuranga il

6
Per chiunque sia interessato, ho appena aggiunto il supporto orizzontale alla libreria.
Michael Frederick,

1
@AlmasAdilbek, puoi spiegare quali parti della tua app hanno subito un calo delle prestazioni? Sarei felice di apportare tutte le ottimizzazioni necessarie.
Michael Frederick,

29

Dai un'occhiata a MMDrawerController:

MMDrawerController

Non siamo riusciti a trovare una biblioteca che ci è piaciuta, quindi abbiamo appena creato la nostra.


10
Volevo solo dire che questa è di gran lunga la migliore implementazione elencata qui. Persone nuove in questa discussione: usa MMDrawerController.
mxcl,

Apprezzo le parole gentili!
Kcharwood,

Stavo usando ViewDeck dall'ultimo anno ma di recente sono passato a MMDrawerController e consiglio a tutti di usarlo. Una delle migliori implementazioni nelle librerie complessive. Grazie Kevin :)
Tariq,

Ho controllato questo MMDrawerController e voglio dire che è davvero la migliore opzione per il menu laterale. Apprezzato! Grazie mille a @Michael Frederick
Resty

Questo supporta iOS8? Grazie
Brad Thomas il

12

L' idea chiave che devi impostare il frame o il centro di self.navigationController.view . Vi sono due eventi che devi gestire. (1) barButtonItem premere . (2) gesto di panoramica a causa dello scorrimento.

Puoi inviare il controller di visualizzazione allo sfondo in questo modo:

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

Funziona perfettamente e senza troppo codice. Devi solo specificare i limiti in cui l'utente può eseguire la panoramica in modo che la tua vista
Moisés Olmedo,

@Janos, signore, mi aiuti gentilmente a mancare di questo per più giorni
Ragul,

Ho creato il menu laterale con l'aiuto del tuo codice sopra, quando slideout il lato posteriore mostra solo il colore scuro, non la vista mi aiuta gentilmente
Ragul

Signore, funziona benissimo. Uso solo questo codice sopra. l'unica cosa è che la vista di sfondo non mostra che l'ho modificata, per favore aiutatemi a mostrare la vista di sfondo
Ragul

@ János e dove utilizzare questa riga di codice [self.view sendSubviewToBack: menuViewController.view];
Ragul,


11

L'implementazione di Facebook inserisce un UIPanGestureRecognizer su UINavigationBar. Permettendo così di catturare i colpi dove è necessario.

Ciò consente di fare cose del genere, riconoscendo direttamente la direzione del tocco in x / z e la velocità con cui si sono verificati.

Anche questo tipo di armeggiamento con UIVview (più di uno sullo schermo alla volta con compiti evidentemente diversi -> quindi controller diversi) dovrebbe (sono tentato di dire deve) utilizzare le nuove funzionalità di iOS ViewController-Containment. Ogni implementazione senza questo è semplicemente sbagliata in quanto armeggia con la gerarchia delle viste in un modo non previsto da Apple.

A proposito: se sei davvero curioso di sapere come farlo nel modo più vicino possibile a Facebook, dai un'occhiata al progetto che ho aperto su Github PKRevealController .


Non c'è nulla di sbagliato (o HIG non conforme) nella gestione dell'interfaccia utente personalizzata basata su UIView. Il contenimento del controller di visualizzazione è arrivato troppo tardi, quindi sono stati utilizzati molti altri approcci. Un articolo correlato: blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers
Jacob Jennings,

1
I principali sviluppi non avranno come obiettivo una versione minima di iOS 5 per molto tempo, dal momento che un gran numero di utenti non effettuerà l'aggiornamento per molto tempo. Mentre molti di loro sono "hack" come dici tu, può certamente essere fatto correttamente. Rimanere all'interno dei framework è sicuramente una scommessa sicura, ma a volte un'interfaccia utente unica e personalizzata richiede di più.
Jacob Jennings,

8

Esistono decine di librerie diverse che gli sviluppatori hanno creato per implementare la navigazione in stile Facebook / Path per le app iOS. Posso elencarli tutti ma, come mi hai chiesto il migliore, ecco le mie due scelte che utilizzo per implementare il menu di navigazione laterale:

1) ECSlidingViewController È anche molto facile da implementare e utilizzare Storyboard. Non ho avuto problemi con l'implementazione né ricevuto errori o simili. Il link che ho fornito ha praticamente tutte le spiegazioni per usarlo.

2) SWRevealViewController Questa libreria è facile da usare e puoi trovare un tutorial QUI , che mostra come implementarlo con tutte le spiegazioni insieme alle immagini. Puoi semplicemente seguire il tutorial e ringraziarmi più tardi.


1
SWRevealViewController è il più semplice, usato senza bug funziona bene
vishal dharankar

7

Un'altra opzione è usare Scringo . Ti offre un menu laterale come nelle app di YouTube / Facebook e anche tutti i tipi di funzionalità integrate nel menu che puoi scegliere di aggiungere (ad esempio chat, invitare amici, ecc ...)

L'aggiunta del menu laterale è semplicemente chiamando [ScringoAgent startSession ...] e tutta la configurazione può essere eseguita sul sito.



4

Questa domanda è piuttosto popolare ma tutto si è tradotto in una facile importazione e utilizzo per me ... Ecco cosa uso ... SWRevealController .

Sono sorpreso che la gente lo manchi ... È davvero FANTASTICO !!! e facile da usare. Lo sviluppatore include anche alcuni progetti di esempio che ti consentono di vedere come usarlo in diversi scenari.



2

Sia Gmail che Facebook fanno un uso intensivo delle visualizzazioni Web, quindi è difficile dire cosa sia il codice nativo e cosa sia reso HTML. Tuttavia, guardando l'interfaccia, sembra che abbiano posizionato un UITableView con una larghezza più stretta della larghezza dello schermo (320pt) sotto un UIView che contiene il contenuto che vogliono visualizzare. La selezione di diverse righe di vista tabella probabilmente sostituisce una vista secondaria della vista contenuto.

Almeno, è così che affronterei il problema. È difficile dettare ciò che dovrebbe essere. Basta entrare subito e iniziare a sperimentare!


ehi qualcuno può dirmi come creare la visualizzazione a scorrimento di tipo facebook e il pannello menu in Android
Neerav Shah,

1

Se vuoi, ho fatto questo repository su gSSub GSSlideMenu Ti permette di creare un menu in stile Facebook MA con un webView "indietro" (in genere tutti i repository che ho trovato hanno un tableView come vista "indietro").



1

la vista a destra potrebbe essere all'interno di una sottoclasse di UIScrollView. In questa sottoclasse è possibile ignorare - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)eventper restituire SÌ solo se l'utente ha toccato la vista secondaria. In questo modo puoi posizionare il tuo UIScrollView trasparente su qualsiasi altra vista e passare attraverso qualsiasi evento touch che si svolge al di fuori della vista secondaria; e ottieni materiale di scorrimento gratis.

Per esempio:

- (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

Prova ad aggiungere il tuo menu (che scorri per arrivare a) sotto la vista principale. Inizia a iscriverti per toccare gli eventi nella vista.

Implementa touchesMoved:e controlla se il primo gestureè verticale (scorri la vista principale, se necessario) o orizzontale (qui vorrai mostrare il menu). Se è orizzontale, inizia a invocare un altro metodo, - (void)scrollAwayMainView:(NSUInteger)pixelsogni volta che touchesMoved:viene chiamato, calcolando il numero di pixel di distanza dal punto iniziale che dovrebbe essere la vista principale e passando quel numero nel metodo. Nell'implementazione di tali metodi, eseguire il codice seguente:

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

0

Dai un'occhiata alla mia soluzione per questa domanda:

Come creare un menu di diapositive personalizzato senza libreria di terze parti.?

Una singola classe che devi solo sottoclassare e compilare con il contenuto.

Ecco la lezione. Per maggiori dettagli, consultare il link sopra.

#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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.