Was ist der beste Weg, um ein Sideswipe-Menü wie das in der neuen iOS-App von Facebook zu entwickeln?

155

Es scheint, dass Side-Swipe-Menüs ein häufigeres Element der Benutzeroberfläche werden, da in jeder iPhone-App mehr Informationen gespeichert werden. Facebook hat es in seine neueste Version aufgenommen und die neue Google Mail-App scheint es ebenfalls aufzunehmen . Ich habe mich gefragt, ob jemand darüber nachgedacht hat, wie so etwas am effizientesten entwickelt werden kann, da es zu einem häufigeren Schnittstellenelement wird. Während ich meine eigenen Gedanken darüber habe, wie ich das bauen soll, bin ich neugierig zu hören, was andere Leute denken.

Facebook Swipe Navigation

Nick ONeill
quelle
Ich habe festgestellt, dass Sie bei aktiviertem Seitenwischmenü nichts mit dem ausgeblendeten Teil der Ansicht tun können. Meine Idee war es, die aktuelle Ansicht im Bild zu rendern und einen Teil davon anzuzeigen, wenn das Seitenwischen aktiviert ist
Denis
5
Wessen Facebook-Profil hast du gesehen, Nick O'Neill? :-p
Constantino Tsarouhas
1
mögliches Duplikat von SplitView aber auf dem iPhone
JosephH
1
@ Zoidberg Ich bin interessiert, mehr darüber zu hören. Persönlich mag ich das Sidenav! Aber ich bin ein Entwickler, kein Designer. Was macht diese UX arm?
Robert Karl
3
Bitte schauen Sie sich Designing Intuitive User Experiences an, um zu erfahren, welche Probleme Apple mit diesen sogenannten "Hamburgermenüs" identifiziert hat
vikingosegundo

Antworten:

34

Ich bin kürzlich darauf gestoßen, habe mir den Code nicht angesehen oder das Steuerelement getestet, aber es sieht so aus, als wäre es ein sehr anständiger Ausgangspunkt.

jtrevealsidebar

Edit: Der Leser sollte sich auch die anderen Antworten ansehen :)

Zaky Deutsch
quelle
Erlaubt
6
Wenn Sie nach Swipe-Funktionen suchen. Fügen Sie einen Gestenerkenner in die Ansicht ein und ersetzen Sie die Umschalttaste durch den Gestenerkenner.
CJ Teuben
Aber was ist, wenn Sie wischen und umschalten möchten?
Jsetting32
Dies wird eingestellt, damit ich woanders hinziehen würde.
Mike Flynn
84

Dafür gibt es eine großartige Bibliothek von Tom Adriaenssen: Inferis / ViewDeck

Es ist sehr einfach zu bedienen und hat eine ziemlich große Fangemeinde.

BEARBEITEN:

Weitere Informationen zu etwas Leichterem finden Sie unter: Mutualmobile / MMDrawerController

Es verfügt nicht über alle Funktionen von ViewDeck, ist jedoch einfacher zu ändern und zu erweitern.

Chris Knadler
quelle
6
Ich empfehle jedem, dies zu benutzen.
Almas Adilbek
3
beste Lösung: flexibel und einfach zu bedienen
HotJard
2
Ich habe es versucht und viele Fehler in ihrem Beispiel gefunden. Nach einigen schnellen Klicks sind die Viewcontroller falsch ausgerichtet
Torsten B
13
Wir haben es für mehrere Apps verwendet, aber es war sehr schwierig, es zu warten und neue Funktionen hinzuzufügen. Es hat auch unter dem Versuch gelitten, zu viel umzusetzen. Wir haben uns entschlossen, unseren eigenen MMDrawerController zu schreiben. Leicht und konzentriert: github.com/mutualmobile/MMDrawerController
kcharwood
1
MMDrawerController ist eine sehr gute Bibliothek. Außerdem unterstützt es die universelle App. iPhone und iPad Apps
Erhan Demirci
48

Ich habe dafür eine Bibliothek erstellt. Es heißt MFSideMenu .

Es unterstützt unter anderem iPhone + iPad, Hoch- und Querformat, Menü auf der linken oder rechten Seite, UITabBarController und Schwenkgesten.

Michael Frederick
quelle
Es ist ein großartiges Projekt. Aber es unterstützt den Landschaftsmodus nicht. Können Sie uns einen Hinweis geben, es auch im Querformat zu verwenden? Danke
Sameera Chathuranga
Danke @SameeraChathuranga. Ich hoffe, Landschaftsunterstützung hinzuzufügen, wenn ich etwas Zeit habe. Der einzige Teil, der keine Landschaftsunterstützung bietet, ist der SideMenuViewController ... Ich glaube, Sie müssten hier so etwas wie die zweite Antwort tun: stackoverflow.com/questions/2508630/… . Fühlen Sie sich frei, sich zu teilen und zum Repo beizutragen!
Michael Frederick
Und ich empfehle, dies ist die Antwort auf diese Frage. Nicht die obige. Es ist zu komplex. Das ist sehr, sehr einfach zu handhaben. Wenn jemand möchte, kann ich den Code posten. Wie arbeite ich mit Oriantation? )
Sameera Chathuranga
6
Für alle Interessierten habe ich der Bibliothek gerade Landschaftsunterstützung hinzugefügt.
Michael Frederick
1
@AlmasAdilbek, können Sie erklären, in welchen Teilen Ihrer App die Leistung abgenommen hat? Gerne nehme ich die notwendigen Optimierungen vor.
Michael Frederick
29

Schauen Sie sich MMDrawerController an:

MMDrawerController

Wir konnten keine Bibliothek finden, die uns gefiel, also haben wir einfach unsere eigene gerollt.

kcharwood
quelle
10
Ich wollte nur sagen, dass dies bei weitem die beste hier aufgeführte Implementierung ist. Personen, die neu in diesem Thread sind: Verwenden Sie MMDrawerController.
mxcl
Schätzen Sie die freundlichen Worte!
Charchar
Ich habe ViewDeck seit einem Jahr verwendet, bin aber kürzlich zu MMDrawerController gewechselt und empfehle jedem, dies zu verwenden. Eine der besten Implementierungen in Gesamtbibliotheken. Danke Kevin :)
Tariq
Ich habe diesen MMDrawerController überprüft und möchte sagen, dass er wirklich die beste Option für das Seitenmenü ist. Geschätzt! Vielen Dank an @Michael Frederick
Resty
Unterstützt dies iOS8? Vielen Dank
Brad Thomas
12

Die Schlüsselidee, dass Sie den Rahmen oder die Mitte von self.navigationController.view festlegen müssen . Es gibt zwei Ereignisse , die Sie behandeln müssen. (1) barButtonItem drücken . (2) pan Geste aufgrund wischen.

Sie können den View Controller wie folgt in den Hintergrund senden:

[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
quelle
Funktioniert perfekt und ohne zu viel Code. Sie müssen nur die Grenzen angeben, an denen der Benutzer schwenken kann, damit Ihre Ansicht
Moisés Olmedo
@ Janos Sir, bitte helfen Sie mir, dies für weitere Tage zu
vermissen
Ich habe das Side-Out-Menü mit Hilfe Ihres obigen Codes erstellt. Wenn auf der Rückseite nur die dunkle Farbe
angezeigt wird,
Sir, es funktioniert gut. Ich verwende nur diesen obigen Code. Die einzige Sache ist, dass die Hintergrundansicht nicht anzeigt, dass ich sie aktualisiert habe. Bitte helfen Sie mir, die Hintergrundansicht anzuzeigen
Ragul
@ János und wo diese Codezeile verwendet werden soll [self.view sendSubviewToBack: menuViewController.view];
Ragul
11

Noch besser ist JASidePanels . Einfach zu implementieren und funktioniert sowohl für iPhone als auch für iPad.

Github-Link: JASidePanels

user2330922
quelle
11

Durch die Implementierung von Facebook wird ein UIPanGestureRecognizer in der UINavigationBar platziert. So können Swipes dort gefangen werden, wo sie gebraucht werden.

Dies ermöglicht beispielsweise das direkte Erkennen der Berührungsrichtung in x / z sowie der Geschwindigkeit, mit der sie aufgetreten sind.

Auch diese Art des Bastelns mit UIViews (mehr als eine auf dem Bildschirm gleichzeitig mit offensichtlich unterschiedlichen Aufgaben -> also unterschiedlichen Controllern) sollte (ich bin versucht zu sagen, muss) die neuen ViewController-Containment-Funktionen von iOS verwenden. Jede Implementierung ohne das ist einfach schlecht, da sie an der Ansichtshierarchie auf eine Weise bastelt, die nicht von Apple beabsichtigt ist.

Nebenbei bemerkt: Wenn Sie wirklich neugierig sind, wie dies so nah wie möglich an Facebook geschehen kann, schauen Sie sich das Projekt an, das ich auf Github PKRevealController geöffnet habe .

pkluz
quelle
Es ist nichts Falsches (oder HIG-nicht konformes) an der Verwaltung der UIView-basierten benutzerdefinierten Benutzeroberfläche. Die Eindämmung des View-Controllers kam zu spät, sodass viele andere Ansätze verwendet wurden. Ein verwandter Artikel: blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers
Jacob Jennings
1
Wichtige Entwicklungen werden lange Zeit nicht auf eine Mindestversion von iOS 5 abzielen, da eine große Anzahl von Benutzern für eine sehr lange Zeit kein Upgrade durchführen wird. Während viele von ihnen "Hacks" sind, wie Sie sagen, kann es sicherlich richtig gemacht werden. Das Einhalten innerhalb von Frameworks ist definitiv eine sichere Sache, aber manchmal erfordert eine einzigartige und benutzerdefinierte Benutzeroberfläche mehr.
Jacob Jennings
8

Es gibt Dutzende verschiedener Bibliotheken, die Entwickler erstellt haben, um die Navigation im Facebook / Path-Stil für iOS-Apps zu implementieren. Ich kann alle auflisten, aber da Sie nach der besten gefragt haben, sind hier meine zwei Auswahlmöglichkeiten, die ich zum Implementieren des Navigationsmenüs mit seitlichem Wischen verwende:

1) ECSlidingViewController Es ist sehr einfach, Storyboards zu implementieren und zu verwenden. Ich hatte keine Probleme bei der Implementierung und habe auch keine Fehler oder ähnliches erhalten. Der Link, den ich bereitgestellt habe, enthält so ziemlich alle Erklärungen, um ihn zu verwenden.

2) SWRevealViewController Diese Bibliothek ist einfach zu bedienen und Sie finden HIER ein Tutorial , das zeigt, wie man es implementiert, mit allen Erklärungen zusammen mit Bildern. Sie können einfach dem Tutorial folgen und sich später bei mir bedanken.

AJ112
quelle
1
SWRevealViewController ist am einfachsten, verwendet es keine Fehler funktioniert gut
vishal dharankar
7

Eine andere Möglichkeit ist die Verwendung von Scringo . Es bietet Ihnen ein Seitenmenü wie in YouTube / Facebook-Apps sowie alle Arten von integrierten Funktionen im Menü, die Sie hinzufügen können (z. B. chatten, Freunde einladen usw.).

Das Hinzufügen des Seitenmenüs erfolgt einfach durch Aufrufen von [ScringoAgent startSession ...]. Die gesamte Konfiguration kann auf der Site vorgenommen werden.

hungary54
quelle
4

Diese Frage ist sehr beliebt, aber es kam alles auf das einfache Importieren und Verwenden für mich an ... Hier ist, was ich benutze ... SWRevealController .

Ich bin überrascht, dass die Leute es vermissen ... Es ist wirklich großartig !!! und einfach zu bedienen. Der Entwickler enthält sogar einige Beispielprojekte, mit denen Sie sehen können, wie Sie es in verschiedenen Szenarien verwenden.

jsetting32
quelle
2

Sowohl in Google Mail als auch in Facebook werden Webansichten häufig verwendet. Daher ist es schwer zu sagen, was nativer Code ist und was HTML gerendert wird. Bei Betrachtung der Benutzeroberfläche sieht es jedoch so aus, als hätten sie eine UITableView mit einer Breite, die schmaler als die Bildschirmbreite (320pt) ist, unter einer UIView platziert, die den Inhalt enthält, den sie anzeigen möchten. Durch Auswahl verschiedener Tabellenansichtszeilen wird wahrscheinlich eine Unteransicht der Inhaltsansicht ausgetauscht.

Zumindest würde ich so mit dem Problem umgehen. Es ist schwer zu diktieren, was es sein soll. Spring einfach rein und fang an zu experimentieren!

Eschenfurche
quelle
Hey, kann mir jemand sagen, wie man die Facebook-Typ Swipe-Ansicht und das Menü-Panel in Android
Neerav Shah
1

Wenn Sie möchten, habe ich dieses Repo auf github GSSlideMenu gemacht. Es ermöglicht Ihnen, ein Menü im Facebook-Stil zu erstellen, ABER mit einer "Zurück" -Webansicht (im Allgemeinen haben alle Repos, die ich gefunden habe, eine Tabellenansicht als "Zurück" -Ansicht).

JAA
quelle
1

Die Ansicht rechts kann sich in einer Unterklasse von UIScrollView befinden. In dieser Unterklasse können Sie überschreiben - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event, um nur dann JA zurückzugeben, wenn der Benutzer die Unteransicht berührt hat. Auf diese Weise können Sie Ihre transparente UIScrollView über jede andere Ansicht platzieren und jedes Berührungsereignis durchlaufen, das außerhalb der Unteransicht stattfindet. und du bekommst kostenlos Scroll-Sachen.

Beispielsweise:

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

oder:

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

Versuchen Sie, Ihr Menü (zu dem Sie streichen) unter der Hauptansicht hinzuzufügen. Beginnen Sie mit dem Abonnieren von Berührungsereignissen in der Ansicht.

Implementieren Sie touchesMoved:und überprüfen Sie, ob die erste gesturevertikal (ggf. in der Hauptansicht scrollen) oder horizontal (hier möchten Sie das Menü anzeigen) ist. Wenn es horizontal ist, rufen Sie bei - (void)scrollAwayMainView:(NSUInteger)pixelsjedem Aufruf eine andere Methode auf, touchesMoved:berechnen Sie die Anzahl der Pixel vom Startpunkt der Hauptansicht entfernt und übergeben Sie diese Zahl an die Methode. Führen Sie in dieser Methodenimplementierung den folgenden Code aus:

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

Schauen Sie sich meine Lösung für diese Frage an:

Wie erstelle ich ein benutzerdefiniertes Folienmenü ohne Bibliothek eines Drittanbieters?

Eine einzelne Klasse, die Sie nur unterordnen und mit Inhalten füllen müssen.

Hier ist die Klasse. Weitere Details finden Sie unter dem obigen 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
David Tarnok
quelle