Facebook의 새로운 iOS 앱과 같은 사이드 와이프 메뉴를 개발하는 가장 좋은 방법은 무엇입니까?


155

사이드 스 와이프 메뉴가 더 많은 정보가 각 iPhone 응용 프로그램에 적용됨에 따라 더욱 일반적인 인터페이스 요소가되고있는 것으로 보입니다. Facebook은 최신 버전으로이를 포함 시켰으며 새로운 Gmail 앱에도 포함되어있는 것으로 보입니다 . 더 일반적인 인터페이스 요소가되고 있기 때문에 누군가가 이와 같은 것을 개발하는 가장 효율적인 방법에 대해 생각하고 있는지 궁금합니다. 나는 이것을 구축하는 방법에 대한 내 자신의 생각을 가지고 있지만 다른 사람들의 생각을 듣고 싶어합니다.

페이스 북 슬쩍 탐색


사이드 와이프 메뉴가 활성화되면 뷰의 일부를 스 와이프하여 아무것도 할 수 없다는 것을 알았습니다. 내 아이디어는 사이드 와이프가 활성화 될 때 이미지에서 현재보기를 렌더링하고 그 부분을 표시하는 것이 었습니다
Denis

5
Nick O'Neill 님, 누구의 Facebook 프로필을 보셨습니까? - P
콘스탄티노 Tsarouhas


1
@Zoidberg 나는 이것에 대해 더 듣고 싶습니다. 개인적으로 사이드 나비를 좋아합니다! 그러나 저는 디자이너가 아닌 개발자입니다. 이 UX의 문제점
Robert Karl

3
사람이 시계하시기 바랍니다 설계 직관적 인 사용자 경험은 문제 사과이 소위 "햄버거 메뉴"로 확인 된 것에 대해 배울 수
vikingosegundo

답변:


34

나는 최근에 이것을 만났고 실제로 코드를 보거나 컨트롤을 테스트하지는 않았지만 아주 괜찮은 출발점이 될 것 같습니다.

jtrevealsidebar

편집 : 독자는 다른 답변도 살펴 봐야합니다 :)


사용자가 슬라이드를 허용하지 않음
cannyboy

6
스 와이프 기능을 찾고 있다면 제스처 인식기를 뷰에 삽입 한 다음 토글 버튼을 제스처 인식기로 바꿉니다.
CJ Teuben

그러나 스 와이프하고 전환하려면 어떻게해야합니까?
jsetting32

이것은 중단되었으므로 다른 곳으로 이동할 것입니다.
Mike Flynn

84

Tom Adriaenssen의 훌륭한 라이브러리가 있습니다 : Inferis / ViewDeck

사용하기가 쉽고 상당히 큰 관심을 끌고 있습니다.

편집하다:

좀 더 가벼운 것을 원한다면 mutualmobile / MMDrawerController를 확인하십시오.

ViewDeck의 모든 기능을 갖추고 있지는 않지만 수정 및 확장이 더 간단합니다.


6
나는 이것을 모두 사용하는 것이 좋습니다.
Almas Adilbek

3
최상의 솔루션 : 유연하고 사용하기
쉬운

2
나는 그것을 시도하고 그들의 예제에서 많은 버그를 발견했다. 몇 번의 빠른 클릭 후 뷰 컨트롤러가 잘못 정렬됩니다
Torsten B

13
우리는 여러 앱에 사용했지만 새로운 기능을 유지 관리하고 추가하는 것은 매우 어려웠습니다. 또한 너무 많은 구현을 시도하는 데 어려움을 겪었습니다. 우리는 우리 자신의 MMDrawerController를 작성하기로 결정했습니다. 경량 및 집중 : github.com/mutualmobile/MMDrawerController
kcharwood을

1
MMDrawerController는 매우 좋은 라이브러리이며 범용 응용 프로그램을 지원합니다. 아이폰과 아이 패드 애플 리케이션
Erhan Demirci

48

이를 위해 라이브러리를 만들었습니다. MFSideMenu 라고 합니다 .

아이폰 + 아이 패드, 세로 + 가로, 왼쪽 또는 오른쪽 메뉴, UITabBarController, 팬 제스처 지원 등이 있습니다.


그 큰 프로젝트 ..하지만 가로 모드 지원하지 않는 .. u는 우리에게 너무 가로 모드 .. 감사합니다 그것을 사용하는 힌트 줄 수
사 미라 Chathuranga

감사합니다 @SameeraChathuranga. 시간이 있으면 조경 지원을 추가하고 싶습니다. 가로 지원이없는 유일한 부분은 SideMenuViewController입니다 ... 나는 당신이 여기에서 두 번째 답변과 같은 것을해야한다고 생각합니다 : stackoverflow.com/questions/2508630/… . 자유롭게 포크하고 레포에 기여하십시오!
Michael Frederick

그리고 나는 이것이이 질문에 대한 대답이라고 추천합니다 .. 위의 질문은 아닙니다.. 너무 복잡합니다 .. 이것은 매우 처리하기 쉽습니다 :) 어떤 사람이 코드를 게시 할 수 있다면, oriantation을 사용하는 방법 .. : )
Sameera Chathuranga 7

6
관심있는 사람은 라이브러리에 가로 지원을 추가했습니다.
Michael Frederick

1
@AlmasAdilbek, 앱의 어떤 부분에서 성능이 저하되었는지 설명 할 수 있습니까? 필요한 최적화를 해 드리겠습니다.
Michael Frederick

29

MMDrawerController를 확인하십시오.

MMDrawerController

우리가 좋아하는 라이브러리를 찾을 수 없었으므로 우리는 우리 자신의 것을 굴 렸습니다.


10
이것이 BY FAR 여기에 나열된 최상의 구현이라고 말하고 싶었습니다. 이 스레드를 처음 사용하는 사람들 : MMDrawerController를 사용하십시오.
mxcl

친절한 말에 감사하십시오!
kcharwood

지난 1 년 동안 ViewDeck을 사용하고 있었지만 최근에 MMDrawerController로 전환했으며이 모든 것을 사용하도록 권장합니다. 전체 라이브러리에서 최상의 구현 중 하나입니다. 감사합니다 Kevin :)
Tariq

이 MMDrawerController를 확인하고 실제로 사이드 메뉴에 가장 적합한 옵션이라고 말하고 싶습니다. 감사합니다! @Michael Frederick
Resty

이것이 iOS8을 지원합니까? 감사합니다
Brad Thomas

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

@Janos 선생님은 더 많은 날 동안 부족한 점을 도와주십시오
Ragul

위의 코드를 사용하여 사이드 아웃 메뉴를 만들었습니다. 슬라이드 아웃 뒷면에 어두운 색상 만 표시되면 친절하게 도움이되지 않습니다.
Ragul

선생님은 잘 작동합니다. 위의 코드 만 사용하십시오. 내가 유일하게 보여준 배경보기는 배경보기를 표시하지 않습니다. 배경보기를 표시하도록 도와주세요.
Ragul

@ János 및이 코드 줄을 사용할 위치 [self.view sendSubviewToBack : menuViewController.view];
Ragul


11

Facebook의 구현은 UINavigationBar에 UIPanGestureRecognizer를 배치합니다. 따라서 필요한 곳에서 스 와이프를 잡을 수 있습니다.

이를 통해 x / z의 터치 방향과 발생 속도를 직접 인식하는 등의 작업을 수행 할 수 있습니다.

또한 UIViews (아마도 다른 작업-> 따라서 다른 컨트롤러로 한 번에 하나 이상의 화면에서)와 같은 종류의 땜질은 iOS의 새로운 ViewController-Containment 기능을 사용해야합니다. 그것없이 모든 구현은 단순히 애플이 의도하지 않은 방식으로 뷰 계층 구조를 고려하기 때문에 나쁘다.

참고 사항 : 가능한 한 Facebook과 가까운 방법으로 궁금한 점이 있다면 Github PKRevealController 에서 오픈 소스 프로젝트를 확인하십시오 .


UIView 기반 사용자 정의 UI 관리에는 아무런 문제가 없습니다 (또는 HIG 비 호환). View 컨트롤러 격리가 너무 늦어서 다른 접근 방식이 많이 사용되었습니다. 관련 기사 : blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers
Jacob Jennings

1
많은 사용자가 매우 오랫동안 업그레이드하지 않기 때문에 주요 개발은 iOS 5의 최소 버전을 오랫동안 대상으로하지 않을 것입니다. 많은 사람들이 당신이 말하는 것처럼 '해킹'이지만 확실하게 올바르게 수행 할 수 있습니다. 프레임 워크에 머무르는 것은 확실히 안전한 방법이지만 때로는 고유하고 사용자 정의 된 UI에 더 많은 것이 필요합니다.
Jacob Jennings

8

iOS 앱용 Facebook / Path 스타일 탐색을 구현하기 위해 개발자가 만든 수십 개의 서로 다른 라이브러리가 있습니다. 나는 그것들을 모두 나열 할 수 있지만 가장 좋은 것을 요구 한 것처럼 측면 스 와이프 탐색 메뉴를 구현하는 데 사용하는 두 가지 선택 사항이 있습니다.

1) ECSlidingViewController 스토리 보드를 구현하고 사용하는 것도 매우 쉽습니다. 나는 그것을 구현하는 데 아무런 문제가 없었거나 오류 나 그와 비슷한 것을받지 못했습니다. 내가 제공 한 링크에는 사용하기 위해 거의 모든 설명이 있습니다.

2) SWRevealViewController 이 라이브러리는 사용하기 쉽고 이미지와 함께 모든 설명으로이를 구현하는 방법을 보여주는 자습서를 여기 에서 찾을 수 있습니다. 당신은 단지 튜토리얼을 따르고 나중에 감사 할 수 있습니다.


1
SWRevealViewController는 가장 간단한 것으로, 버그가 잘 작동하지 않습니다
vishal dharankar

7

또 다른 옵션은 Scringo 를 사용하는 입니다. youtube / facebook 앱과 같은 사이드 메뉴와 추가 할 수있는 메뉴의 모든 내장 기능 (예 : 채팅, 친구 초대 등)을 제공합니다.

사이드 메뉴를 추가하면 [ScringoAgent startSession ...]을 호출하기 만하면 사이트에서 모든 구성을 수행 할 수 있습니다.



4

이 질문은 매우 인기가 있지만 모두 가져 오기 및 사용이 쉬워졌습니다 ... 여기 내가 사용하는 것이 있습니다 ... SWRevealController .

나는 사람들이 그것을 그리워하는 것에 놀랐다. .. 그것은 정말로 위대하다! !! 사용하기 쉽습니다. 개발자는 다른 시나리오에서 사용하는 방법을 볼 수있는 몇 가지 예제 프로젝트도 포함합니다.



2

Gmail과 Facebook은 모두 웹보기를 많이 사용하므로 기본 코드 및 HTML 렌더링 내용을 말하기가 어렵습니다. 그러나 인터페이스를 보면 표시하려는 내용이 포함 된 UIView 아래의 화면 너비 (320pt)보다 좁은 너비의 UITableView를 배치 한 것처럼 보입니다. 다른 테이블 뷰 행을 선택하면 내용 뷰의 하위 뷰가 교체 될 수 있습니다.

적어도 그것이 내가 문제에 접근하는 방법입니다. 그것이 무엇인지 지시하기는 어렵습니다. 바로 들어가서 실험을 시작하십시오!


헤이 중 하나는 어떻게 페이스 북의 유형 슬쩍보기와 안드로이드의 메뉴 패널을 만드는 방법 말해 줄 수
Neerav 샤

1

내가 github GSSlideMenu 에서이 repo를 원한다면 "back"webView (일반적으로 내가 찾은 모든 repos는 tableView를 "back"보기로 사용)로 Facebook 스타일 메뉴 BUT을 만들 수 있습니다.



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];

1

기본보기 아래에 메뉴 (스 와이프하여 스 와이프)를 추가해보십시오. 보기에서 이벤트를 터치하여 구독을 시작하십시오.

를 구현 touchesMoved:하고 첫 번째 gesture가 수직인지 (필요한 경우 기본보기를 스크롤) 수평인지 (여기서 메뉴를 표시 하려는지) 확인하십시오. 수평 인 경우, 호출 - (void)scrollAwayMainView:(NSUInteger)pixels될 때마다 다른 메소드를 호출하기 시작 touchesMoved:하고 메인 뷰에서 시작점에서 떨어진 픽셀 수를 계산 한 다음 해당 숫자를 메소드에 전달하십시오. 해당 메소드 구현에서 다음 코드를 실행하십시오.

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

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.