Какой лучший способ разработать боковое меню, как в новом iOS-приложении Facebook?

155

Похоже, что боковые меню становятся все более распространенным элементом интерфейса, так как больше информации попадает в каждое приложение для iPhone. Facebook включил его в свою последнюю версию, и новое приложение Gmail также включает его . Мне было интересно, есть ли у кого-нибудь мысли о наиболее эффективном способе разработки чего-то подобного, поскольку это становится более распространенным элементом интерфейса. Хотя у меня есть свои мысли о том, как это построить, мне любопытно услышать, что думают другие люди.

Навигация смахивания Facebook

Ник ONeill
источник
Я заметил, что когда активировано боковое меню, вы ничего не можете сделать с вытянутой частью вида. Моя идея состояла в том, чтобы визуализировать текущий вид на изображении и отобразить его часть, когда активирована боковая очистка
Денис
5
В чей профиль на Facebook ты заглянул, Ник О'Нил? :-p
Константино Царухас
1
возможный дубликат SplitView, но на iPhone
JosephH
1
@Zoidberg Мне интересно узнать больше об этом. Лично мне нравится сиденав! Но я разработчик, а не дизайнер. Что делает этот UX бедным?
Роберт Карл
3
Пожалуйста, кто-нибудь посмотрит «
Создание интуитивно понятного

Ответы:

34

Я недавно сталкивался с этим, на самом деле не смотрел на код и не тестировал элемент управления, но похоже, что это может быть очень неплохой отправной точкой.

jtrevealsidebar

Изменить: читатель также должен взглянуть на другие ответы :)

Заки немецкий
источник
Не позволяет пользователю скользить
cannyboy
6
Если вы ищете функциональность салфетки. Вставьте распознаватель жестов в представление, затем замените кнопку переключения на распознаватель жестов.
CJ Teuben
Но что, если вы хотите провести и уметь переключаться?
jsetting32
Это прекращено, поэтому я бы переехал в другое место.
Майк Флинн
84

Том Адриаенсен ( Tom Adriaenssen) предлагает для этого отличную библиотеку: Inferis / ViewDeck

Он очень прост в использовании и имеет довольно много подписчиков.

РЕДАКТИРОВАТЬ:

Для чего-то более легкого, проверьте: alternmobile / MMDrawerController

Он не обладает всеми функциями ViewDeck, но его проще модифицировать и расширять.

Крис Нэдлер
источник
6
Я рекомендую всем использовать это.
Алмас Адильбек
3
лучшее решение: гибкий и простой в использовании
HotJard
2
Я попробовал это и нашел много ошибок в их примере. После нескольких быстрых щелчков контроллеры представления смещены
Torsten B
13
Мы использовали его для нескольких приложений, но его было очень сложно поддерживать и добавлять новые функции. Он также пострадал от попыток реализовать слишком много. Мы решили написать собственный MMDrawerController. Легкий и сфокусированный: github.com/mutualmobile/MMDrawerController
kcharwood
1
MMDrawerController - очень хорошая библиотека. Также она поддерживает универсальное приложение. приложения для iphone и ipad
Эрхан Демирчи
48

Я создал библиотеку для этого. Это называется MFSideMenu .

Среди прочего он включает поддержку iphone + ipad, портрет + пейзаж, меню слева или справа, UITabBarController и жесты панорамирования.

Майкл Фредерик
источник
Это отличный проект ... но он не поддерживает ландшафтный режим. Можете ли вы дать нам подсказку, чтобы использовать его и в ландшафтном режиме? Спасибо
Sameera Chathuranga
Спасибо @SameeraChathuranga. Я надеюсь добавить поддержку ландшафта, если у меня будет время. Единственная часть, которая не имеет поддержки альбомной ориентации, - это SideMenuViewController ... Я полагаю, вам придется сделать что-то вроде второго ответа: stackoverflow.com/questions/2508630/… . Не стесняйтесь раскошелиться и внести свой вклад в репо!
Майкл Фредерик
И я рекомендую, что это ответ на этот вопрос .. не вышеупомянутый .. он слишком сложный .. с этим очень легко справиться :) если кто-то захочет опубликовать код, как работать с ориентацией ..: )
Самера Чатуранга
6
Для тех, кто заинтересован, я просто добавил поддержку библиотеки ландшафта.
Майкл Фредерик
1
@AlmasAdilbek, можете ли вы объяснить, в каких частях вашего приложения произошло снижение производительности? Я был бы счастлив сделать любые необходимые оптимизации.
Майкл Фредерик
29

Проверьте MMDrawerController:

MMDrawerController

Мы не смогли найти библиотеку, которая нам понравилась, поэтому мы просто свернули свою собственную.

kcharwood
источник
10
Просто хотел сказать, что это ДАЛЕКО лучшая реализация из перечисленных здесь. Новички в этой теме: используйте MMDrawerController.
mxcl
Цените добрые слова!
Кчарвуд
Я использовал ViewDeck с прошлого года, но недавно я переключился на MMDrawerController, и я рекомендую всем использовать это. Одна из лучших реализаций в общих библиотеках. Спасибо Кевин :)
Tariq
Проверил этот MMDrawerController и хочу сказать, что это действительно лучший вариант для бокового меню. Оценил! Большое спасибо @Michael Frederick
Resty
Это поддерживает iOS8? Спасибо
Брэд Томас
12

Основная идея , что вы должны кадру или центр набора self.navigationController.view в . Есть два события, которые вы должны обработать. (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);
            }];
        }
    }
}
Янош
источник
Работает отлично и без лишнего кода. Вам нужно только указать пределы, где пользователь может панорамировать, чтобы ваш взгляд
Moisés Olmedo
@ Джанос, сэр, помогите мне, но мне не хватает этого на несколько дней
Рагул
Я создал боковое меню с помощью приведенного выше кода, когда задняя панель показывает только темный цвет, а вид, пожалуйста, не помогите мне
Ragul
Сэр, он работает нормально, я использую только этот код выше. единственное, что фоновое представление не показывает, что я обновил его, пожалуйста, помогите мне показать фоновое представление
Ragul
@ Янош и где использовать эту строку кода [self.view sendSubviewToBack: menuViewController.view];
Рагул
11

Еще лучше JASidePanels . Легко реализуется и работает как для iPhone, так и для iPad.

Github ссылка: JASidePanels

user2330922
источник
11

Реализация Facebook размещает UIPanGestureRecognizer на UINavigationBar. Таким образом, позволяя ловить удары, где это необходимо.

Это позволяет делать такие вещи, как прямое распознавание направления касания в x / z, а также скорость, с которой они произошли.

Кроме того, такой вид работы с UIViews (более одного на экране одновременно с явно разными задачами -> таким образом, разными контроллерами) должен (я испытываю желание сказать, должен) использовать новые функции iOS ViewController-Containment. Любая реализация без этого просто плоха, поскольку она влияет на иерархию представлений способом, не предназначенным для Apple.

Об одном замечании: если вам действительно интересно, как это можно сделать как можно ближе к Facebook, ознакомьтесь с проектом, который я открывал, на Github PKRevealController .

pkluz
источник
В управлении пользовательским интерфейсом на основе UIView нет ничего плохого (или HIG-несовместимого). Сдерживание контроллера представления пришло слишком поздно, поэтому было использовано множество других подходов. Связанная статья: blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers
Джейкоб Дженнингс
1
Крупные разработки не будут ориентированы на минимальную версию iOS 5 в течение длительного времени, так как большое количество пользователей не будут обновляться в течение очень долгого времени. Хотя многие из них, как вы говорите, «хаки», это, безусловно, можно сделать правильно. Оставаться в рамках - определенно безопасная ставка, но иногда уникальный и пользовательский интерфейс требует большего.
Джейкоб Дженнингс
8

Разработчики создали десятки различных библиотек для реализации навигации в стиле Facebook / Path для приложений iOS. Я могу перечислить все из них, но, как вы просили лучший, вот мои два варианта, которые я использую для реализации бокового навигационного меню:

1) ECSlidingViewController Очень легко реализовать и использовать раскадровки. У меня не было проблем с его реализацией, я не получал ошибок или чего-то подобного. Ссылка, которую я предоставил, содержит почти все объяснения для ее использования.

2) SWRevealViewController Эта библиотека проста в использовании, и вы можете найти учебник ЗДЕСЬ , который показывает, как ее реализовать, со всеми пояснениями вместе с изображениями. Вы можете просто следовать учебнику и поблагодарить меня позже.

AJ112
источник
1
SWRevealViewController - самый простой, он не
содержит
7

Другой вариант - использовать Scringo . Это дает вам боковое меню, как в приложениях YouTube / Facebook, а также все виды встроенных функций в меню, которые вы можете добавить (например, чат, пригласить друзей и т. Д.)

Добавление бокового меню осуществляется простым вызовом [ScringoAgent startSession ...], и все настройки можно выполнить на сайте.

hungary54
источник
4

Этот вопрос довольно популярен, но все сводилось к тому, что мне легко импортировать и использовать ... Вот что я использую ... SWRevealController .

Я удивлен, что люди скучают по нему ... Это действительно здорово !!! и прост в использовании. Разработчик даже включает несколько примеров проектов, которые позволяют увидеть, как использовать его в различных сценариях.

jsetting32
источник
2

И Gmail, и Facebook активно используют веб-представления, поэтому сложно сказать, что является нативным кодом и что представляет собой HTML. Однако, глядя на интерфейс, кажется, что они поместили UITableView с шириной, более узкой, чем ширина экрана (320pt), под UIView, который содержит контент, который они хотят отобразить. Выбор разных строк табличного представления, вероятно, заменяет подпредставление представления содержимого.

По крайней мере, так я подойду к проблеме. Трудно диктовать, что это должно быть. Просто прыгайте прямо и начинайте экспериментировать!

Ясень борозда
источник
эй может кто-нибудь сказать мне, как создать вид смахивания типа facebook и панель меню в Android
Neerav Shah
1

Если вы хотите, я сделал это репозиторий на github GSSlideMenu. Он позволяет вам создавать меню в стиле Facebook, НО с веб-представлением «назад» (как правило, все обнаруженные мной репозитории имеют табличное представление как представление «назад»).

JAA
источник
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];
javieralog
источник
1

Попробуйте добавить свое меню (к которому вы проводите пальцем) под основным видом. Начните подписку на сенсорные события в представлении.

Реализуйте touchesMoved:и проверьте, является ли первый gestureвертикалью (прокрутите главный вид, если необходимо) или горизонтальным (здесь вы хотите показать меню). Если он горизонтальный, начните вызывать другой метод, - (void)scrollAwayMainView:(NSUInteger)pixelsвсякий раз, когда вызывается touchesMoved:, вычисляя количество пикселей от начальной точки, которым должен быть основной вид, и передавая это число в метод. В реализации этих методов запустите следующий код:

[mainView scrollRectToVisible:CGRectMake:(pixels, 
                                          mainView.origin.y, 
                                          mainView.size.width, 
                                          mainView.size.height];
aopsfan
источник
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
Дэвид Тарнок
источник