开发一个像 Facebook 新 iOS 应用程序那样的干扰菜单的最佳方法是什么?

随着越来越多的信息填充到每个 iPhone 应用程序中,侧滑菜单似乎正在成为一个越来越常见的界面元素。Facebook 已经把它包含在他们的最新版本和 新的 Gmail 应用程序似乎也包含了这个功能中。我想知道是否有人对开发这样的东西的最有效的方法有什么想法,因为它正在成为一个更常见的接口元素。虽然我有自己的想法,如何建立这一点,我很好奇,听听其他人的想法。

Facebook swipe navigation

95488 次浏览

Gmail 和 Facebook 都大量使用 web 视图,所以很难说什么是原生代码,什么是呈现的 HTML。然而,看看界面,他们似乎已经放置了一个宽度比屏幕宽度(320pt)更窄的 UITableView,在 UIView 下面包含他们想要显示的内容。选择不同的表视图行可能会取代内容视图的子视图。

至少,这是我解决问题的方法。很难决定它应该是什么样的。直接跳进去开始实验吧!

右边的视图可能位于 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];

我最近遇到了这个问题,并没有真正地查看代码或测试控件,但看起来它可能是一个非常不错的起点。

侧边栏

编辑: 读者还应该看看其他答案:)

试着在主视图下面添加你的菜单。开始订阅视图中的触摸事件。

实现 touchesMoved:,并检查第一个 gesture是垂直的(如果需要,滚动主视图)还是水平的(这里您想显示菜单)。如果它是水平的,那么每当调用 touchesMoved:时,就开始调用另一个方法 - (void)scrollAwayMainView:(NSUInteger)pixels,计算主视图应该在的起始点以外的像素数,并将这个数字传递给方法。在该方法实现中,运行以下代码:

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

Facebook 的实现在 UINavigationBar 上放置了一个 UIPanGestureIdentiizer,这样就可以在需要的地方抓取滑动信息。

这样就可以直接识别 x/z 中的触摸方向以及触摸速度。

同样,这种对 UIviews 的修补(在屏幕上同时使用不同的任务-> 因此不同的控制器)也应该(我想说必须)使用 iOS 新的 ViewController-遏制特性。没有这个功能的每个实现都很糟糕,因为它以苹果公司不希望的方式修补了视图层次结构。

旁注: 如果你真的很好奇如何尽可能接近 Facebook 的方式来完成它,请查看我在 Github PKRevealController上开源的项目。

我为此创建了一个库,名为 MFSideMenu

除此之外,它还支持 iphone + ipad、肖像 + 风景、左右菜单、 UITabBarController 和平底锅手势。

如果你愿意,我在 github 上做了这个回购 它允许你创建一个 Facebook 风格的菜单,但是有一个“背面”的 webView (通常我发现的所有回购都有一个 tableView 作为“背面”视图)。

有一个伟大的图书馆为此汤姆 Adriaenssen: 地下室/视图层

它非常容易使用,并且拥有相当多的追随者。

编辑:

如果想了解更轻量级的内容,请查看: 互动/多媒体绘图机控制器

它不具备 ViewDeck 的所有特性,但是更容易修改和扩展。

另一种选择是使用 斯克林戈。它提供了一个类似于 youtube/facebook 应用程序的副菜单,以及所有你可以选择添加的菜单内置功能(例如聊天,邀请朋友等等)。.)

添加侧面菜单只需调用[ ScringoAgent startSession... ] ,所有配置都可以在站点上完成。

更好的是 JASidePanels,易于实现,可以在 iPhone 和 iPad 上运行。

Github 链接: JASidePanels

查看 MMDrawerController:

绘图控制器

我们找不到我们喜欢的图书馆,所以我们自己建了一个。

这里有一个很好的出发点: 滑出像 facebook 和 path 这样的导航栏

这个问题是相当流行,但它所有归结为容易导入和使用为我... 这里是我使用... SWRevealController

我很惊讶人们会错过它... 它真的很棒! ! !而且容易使用。开发人员甚至还包括一些示例项目,让您了解如何在不同的场景中使用它。

你必须得到的 关键点子。你必须处理 两件事。(1) .(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);
}];
}
}
}

这正是 Facebook 侧边栏的翻版: 导航库

非常容易使用

为了实现 iOS 应用程序的 Facebook/Path 风格导航,开发人员已经创建了数十个不同的库。我可以列出所有这些,但是正如你所要求的最好的一个,这里是我的两个选择,我用来实现侧滑导航菜单:

1) < a href = “ https://github.com/edgecase/ECSlidingViewController”rel = “ norefrer”> ECSlidingViewController 它非常容易实现和使用故事板也。我没有遇到任何实现它的问题,也没有收到任何错误或类似的东西。为了使用它,我提供的链接几乎有所有的解释。

2) < a href = “ https://github.com/John-Lluch/SWReveaViewController”rel = “ norefrer”> SWReveaViewController 这个库很容易使用,您可以找到一个教程 给你,它显示了如何实现它与所有的解释连同图像。你可以按照教程做,然后再谢我。

看看我对这个问题的解答:

如何创建自定义幻灯片菜单没有第三方库。 ? ?

一个只需要子类化并用内容填充的类。

下面是这个类。有关详细信息,请参阅上面的链接。

#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