资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

ios开发侧滑菜单,安卓开发侧滑菜单

Airbnb for iOS 的侧滑导航菜单是怎么实现的

几个月前曾经粗略的阅读过源码,那时大致思路似乎是这样:

主要从事网页设计、PC网站建设(电脑版网站建设)、wap网站建设(手机版网站建设)、响应式网站开发、程序开发、微网站、小程序制作等,凭借多年来在互联网的打拼,我们在互联网网站建设行业积累了丰富的成都网站建设、网站制作、网络营销经验,集策划、开发、设计、营销、管理等多方位专业化运作于一体,具备承接不同规模与类型的建设项目的能力。

一个RootViewController持有一个LeftMenuView和ContentView,均是RootView的SubView,并在初始化的时候用addChildViewController添加LeftMenu和Content的ViewController。

通过GestureRecognizer根据需求调整View的Alpha,然后加CGAffineTransform并Animate。左划就缩放+平移ContentView然后调整LeftMenuView的Alpha为1,划回来则是相反,期间还有一个侧滑菜单文字的缩放特效。

ContentViewController通常是个NavController,通过LeftMenuView用Storyboard初始化根视图控制器的。

Airbnb的侧滑,在滑动的动画效果里应该还加了Transform3DMakeRotation实现透视效果的样子。

个人感觉或者猜测左下角这个下三角可能是起到“重置LeftMenu和Content”的效果吧。谢邀..个人水平一般般并没有逐行仔细阅读源码,如果有疏漏还请纠正。

iOS开发中 使用导航栏侧滑功能实现pop 两层以上的控制器该如何实现

1、SliderNavigation拥有三个子视图:leftView,rightView,mainView。左右滑动时就通过这三个视图之间层次关系的切换来实现。

2、其实只有上述三个视图完全够了,但是又另外加上了三个属性:leftVC,rightVC,mainVC。这样做的目的是简化操作,同时mainVC还有记录已展示过的视图的任务,这样所有视图都可以通过左右滑动唤出导航栏来了。这样每个子视图上展示的是对应控制器的视图,即[leftView addSubview:leftVC.view];,其他类似。

3、当向左滑动时,调整视图层级关系,因为向左滑动是展示右视图,所以将leftView调整到最底层,同时让mainView随手指移动,这样mainView之下的rightView就展示出来了。

4、有了上述三点,接下来就可以通过给各个环节添加动画来实现好看的效果了。

三、接口定义

.h文件中定义好外界可以自定义的一些属性。

首先是三个控制器

?

1

2

3

4

//左右控制器与主控制器

@property (strong, nonatomic) UIViewController *leftController;

@property (strong, nonatomic) UIViewController *rightController;

@property (strong, nonatomic) UIViewController *mainController;

其次是左右视图的一些相关设定,有判断点、便宜量、动画时间、能否被拉出等

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

//左右视图被拉出以后主视图的X方向的offset(正值)

@property (assign, nonatomic) CGFloat leftOffsetX;

@property (assign, nonatomic) CGFloat rightOffsetX;

//左右视图被拉的过程中的判断点的X值(正值)

@property (assign, nonatomic) CGFloat leftJudgeX;

@property (assign, nonatomic) CGFloat rightJudegX;

//左右视图拉出所用的时间

@property (assign, nonatomic) NSTimeInterval leftOpenDuration;

@property (assign, nonatomic) NSTimeInterval rightOpenDuration;

//左右视图收回时所用的时间

@property (assign, nonatomic) NSTimeInterval leftCloseDuration;

@property (assign, nonatomic) NSTimeInterval rightCloseDuration;

//左右视图被拉出以后主视图放缩的比例(0到1)

@property (assign, nonatomic) CGFloat rightScale;

@property (assign, nonatomic) CGFloat leftScale;

//左右视图能否被拉出

@property (assign, nonatomic) BOOL canShowRight;

@property (assign, nonatomic) BOOL canShowLeft;

刚才也说过,mainVC要记下已经展示过的主视图,可以将这些加入到字典中,这样做的作用是下次可以方便的展示出来。另外,让每一个想展示的视图对应的控制器赋值给mainVC可以实现在所有界面中都能通过左右拉来叫出导航栏的功能。什么意思呢?最根部的依旧是我们封装的sliderNavigation类,其上图的层次依旧存在,只是改变了mainVC的值,这样给用户的体验就是,虽然主界面变了,但依然可以拉出左右导航栏来。

为此我们设置一个字典来保存已经展示过的控制器

?

1

2

//用以记录被当做主控制器展示主视图过的控制器

@property (strong, nonatomic) NSMutableDictionary *controllersDict;

接下来是几个public方法声明,将这种Manager性质的类作为单例,暴露出其展示左右视图的功能供按钮控制,然后是可以让其展示自定义类作为主界面。

?

1

2

3

4

5

6

7

8

9

//单例

+ (id)sharedInstance;

//展示左右视图

- (void)showLeftView;

- (void)showRightView;

//展示自定义类的主视图,参数:自定义类名

- (void)showContentViewWithModel:(NSString *)className;

四、具体实现

首先定义一些常量

?

1

2

3

4

5

6

7

8

9

//制造反弹的动态效果,当通过按钮叫出导航栏时有效

static const CGFloat kOpenSpringDamping = 0.65f;

static const CGFloat kOpenSpringVelocity = 0.10f;

//定义常量表示拉动方向

typedef NS_ENUM(NSUInteger, sliderMoveDirection) {

SliderMoveDirectionLeft = 0,

SliderMoveDirectionRight,

};

然后重点这里讲一下关键代码或方法,其余的讲一下思路

我们可以在初始化方法中将接口中声明的变量赋默认值,当用户没有为这些值赋值时便可以用这些默认值

首先我们初始化三个子视图为屏幕大小并根据添加到sliderNavigation的子视图中,注意添加顺序:我们希望让主视图在最上方,所以前两个随意,主视图必须最后添加。

?

1

2

3

4

5

6

7

8

9

10

11

12

- (void)_initSubviews

{

_rightView = [[UIView alloc] initWithFrame:self.view.bounds];

[self.view insertSubview:_rightView atIndex:0];

_leftView = [[UIView alloc] initWithFrame:self.view.bounds];

[self.view insertSubview:_leftView atIndex:1];

//主视图要最后添加(即添加到最上面显示)

_mainView = [[UIView alloc] initWithFrame:self.view.bounds];

[self.view insertSubview:_mainView aboveSubview:_leftView];

}

然后我们初始化左右控制器,将左右控制器视图分别添加到左右视图中去。

在实现上述public方法“展示自定义类的主视图”时,传入参数为类名,将其作为键来从字典中取控制器,如果没有则以此类名新建一个控制器并加入到字典中。如果当前主视图上已经有视图,则将其移除。接着将自定义类的视图添加到mainView上,并相应赋值。

当然,不要忘了关闭左右导航栏(因为展示的类有可能是通过左右导航栏点出来的)

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

- (void)showContentViewWithModel:(NSString *)className

{

[self _closeSliderNavigation];

UIViewController *controller = [self.controllersDict objectForKey:className];

if (controller == nil) {

Class c = NSClassFromString(className);

controller = [[c alloc] init];

[self.controllersDict setObject:controller forKey:className];

}

//如果当前已经有视图被显示,则将其取消

if (_mainView.subviews.count 0) {

[[_mainView.subviews firstObject] removeFromSuperview];

}

controller.view.frame = _mainView.frame;

[_mainView addSubview:controller.view];

self.mainController = controller;

}

接着是动画,这里用到的动画主要就是改变视图的大小和位置,用transform即可。获得transform的方法单独抽出来,使用concat将大小变换矩阵和位置变换矩阵连接。接着在动画块中改变主视图的transform即可,当然了,也可以设置上阴影效果等。需要注意的是要根据滑动方向将相应视图调整到最底层。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

CGAffineTransform concat = [self _transformWithMoveDirection:SliderMoveDirectionLeft];

[self.view sendSubviewToBack:_leftView];span style="white-space:pre" /span //将另一个视图调到最下面

[self _configureViewShadowWithDirection:SliderMoveDirectionLeft]; //设置阴影

[UIView animateWithDuration:self.rightOpenDuration

delay:0

usingSpringWithDamping:kOpenSpringDampingspan style="white-space:pre" /span //弹性效果

initialSpringVelocity:kOpenSpringVelocity

options:UIViewAnimationOptionCurveLinear

animations:^{

_mainView.transform = concat;

}

completion:^(BOOL finished) {

_showingLeft = NO;

_showingRight = YES;

self.mainController.view.userInteractionEnabled = NO;

_tapGesture.enabled = YES;

}];

另一方向的雷同

最主要的还是滑动手势操作,也是比较麻烦的地方。不过其实思路比较清晰:获取偏移量,在滑动时计算出对应的变换矩阵并设置,在滑动结束时根据位置与判断点的关系做出相应的动画调整。

例如,滑动过程中向右拉时:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

CGFloat translateX = [recognizer translationInView:_mainView].x;

translateX += currentOffsetX;

float scale = 0;

//向右拉,展示的是左视图

if (translateX 0) {

if (self.canShowLeft == NO || self.leftController == nil) {

return;

}

//将右视图放到底部以将左视图显示出来

[self.view sendSubviewToBack:_rightView];

[self _configureViewShadowWithDirection:SliderMoveDirectionRight];

if (_mainView.frame.origin.x self.leftOffsetX) {

scale = 1 - (_mainView.frame.origin.x / self.leftOffsetX) * (1 - self.leftScale);

} else {

scale = self.leftScale;

}

} else if (translateX 0) {……}

比较头痛的十scale的计算。这里的要求是当view从最初到最末时scale的变化为1.0到self.leftScale,因此利用数学知识推出这个公式即可。上述代码省略了向左拉的代码。

而在拉动结束状态则与左拉右拉动画实现类似。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

CGFloat translateX = [recognizer translationInView:_mainView].x;

translateX += currentOffsetX;

if (translateX self.leftJudgeX) {

if (self.canShowLeft == NO || self.leftController == nil) {

return;

}

CGAffineTransform trans = [self _transformWithMoveDirection:SliderMoveDirectionRight];

[UIView beginAnimations:nil context:nil];

_mainView.transform = trans;

[UIView commitAnimations];

_showingLeft = YES;

_showingRight = NO;

self.mainController.view.userInteractionEnabled = NO;

_tapGesture.enabled = YES;

} else if (translateX -self.rightJudgeX) {……}

ios 怎么做到每个页面都有侧滑栏

1、SliderNavigation拥有三个子视图:leftView,rightView,mainView。左右滑动时就通过这三个视图之间层次关系的切换来实现。

2、其实只有上述三个视图完全够了,但是又另外加上了三个属性:leftVC,rightVC,mainVC。这样做的目的是简化操作,同时mainVC还有记录已展示过的视图的任务,这样所有视图都可以通过左右滑动唤出导航栏来了。这样每个子视图上展示的是对应控制器的视图,即[leftView addSubview:leftVC.view];,其他类似。

3、当向左滑动时,调整视图层级关系,因为向左滑动是展示右视图,所以将leftView调整到最底层,同时让mainView随手指移动,这样mainView之下的rightView就展示出来了。

4、有了上述三点,接下来就可以通过给各个环节添加动画来实现好看的效果了。

三、接口定义

.h文件中定义好外界可以自定义的一些属性。

首先是三个控制器

?

1

2

3

4

//左右控制器与主控制器

@property (strong, nonatomic) UIViewController *leftController;

@property (strong, nonatomic) UIViewController *rightController;

@property (strong, nonatomic) UIViewController *mainController;

其次是左右视图的一些相关设定,有判断点、便宜量、动画时间、能否被拉出等

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

//左右视图被拉出以后主视图的X方向的offset(正值)

@property (assign, nonatomic) CGFloat leftOffsetX;

@property (assign, nonatomic) CGFloat rightOffsetX;

//左右视图被拉的过程中的判断点的X值(正值)

@property (assign, nonatomic) CGFloat leftJudgeX;

@property (assign, nonatomic) CGFloat rightJudegX;

//左右视图拉出所用的时间

@property (assign, nonatomic) NSTimeInterval leftOpenDuration;

@property (assign, nonatomic) NSTimeInterval rightOpenDuration;

//左右视图收回时所用的时间

@property (assign, nonatomic) NSTimeInterval leftCloseDuration;

@property (assign, nonatomic) NSTimeInterval rightCloseDuration;

//左右视图被拉出以后主视图放缩的比例(0到1)

@property (assign, nonatomic) CGFloat rightScale;

@property (assign, nonatomic) CGFloat leftScale;

//左右视图能否被拉出

@property (assign, nonatomic) BOOL canShowRight;

@property (assign, nonatomic) BOOL canShowLeft;

刚才也说过,mainVC要记下已经展示过的主视图,可以将这些加入到字典中,这样做的作用是下次可以方便的展示出来。另外,让每一个想展示的视图对应的控制器赋值给mainVC可以实现在所有界面中都能通过左右拉来叫出导航栏的功能。什么意思呢看最根部的依旧是我们封装的sliderNavigation类,其上图的层次依旧存在,只是改变了mainVC的值,这样给用户的体验就是,虽然主界面变了,但依然可以拉出左右导航栏来。

为此我们设置一个字典来保存已经展示过的控制器

?

1

2

//用以记录被当做主控制器展示主视图过的控制器

@property (strong, nonatomic) NSMutableDictionary *controllersDict;

接下来是几个public方法声明,将这种Manager性质的类作为单例,暴露出其展示左右视图的功能供按钮控制,然后是可以让其展示自定义类作为主界面。

?

1

2

3

4

5

6

7

8

9

//单例

+ (id)sharedInstance;

//展示左右视图

- (void)showLeftView;

- (void)showRightView;

//展示自定义类的主视图,参数:自定义类名

- (void)showContentViewWithModel:(NSString *)className;

四、具体实现

首先定义一些常量

?

1

2

3

4

5

6

7

8

9

//制造反弹的动态效果,当通过按钮叫出导航栏时有效

static const CGFloat kOpenSpringDamping = 0.65f;

static const CGFloat kOpenSpringVelocity = 0.10f;

//定义常量表示拉动方向

typedef NS_ENUM(NSUInteger, sliderMoveDirection) {

SliderMoveDirectionLeft = 0,

SliderMoveDirectionRight,

};

然后重点这里讲一下关键代码或方法,其余的讲一下思路

我们可以在初始化方法中将接口中声明的变量赋默认值,当用户没有为这些值赋值时便可以用这些默认值

首先我们初始化三个子视图为屏幕大小并根据添加到sliderNavigation的子视图中,注意添加顺序:我们希望让主视图在最上方,所以前两个随意,主视图必须最后添加。

?

1

2

3

4

5

6

7

8

9

10

11

12

- (void)_initSubviews

{

_rightView = [[UIView alloc] initWithFrame:self.view.bounds];

[self.view insertSubview:_rightView atIndex:0];

_leftView = [[UIView alloc] initWithFrame:self.view.bounds];

[self.view insertSubview:_leftView atIndex:1];

//主视图要最后添加(即添加到最上面显示)

_mainView = [[UIView alloc] initWithFrame:self.view.bounds];

[self.view insertSubview:_mainView aboveSubview:_leftView];

}

然后我们初始化左右控制器,将左右控制器视图分别添加到左右视图中去。

在实现上述public方法逗展示自定义类的主视图地时,传入参数为类名,将其作为键来从字典中取控制器,如果没有则以此类名新建一个控制器并加入到字典中。如果当前主视图上已经有视图,则将其移除。接着将自定义类的视图添加到mainView上,并相应赋值。

当然,不要忘了关闭左右导航栏(因为展示的类有可能是通过左右导航栏点出来的)

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

- (void)showContentViewWithModel:(NSString *)className

{

[self _closeSliderNavigation];

UIViewController *controller = [self.controllersDict objectForKey:className];

if (controller == nil) {

Class c = NSClassFromString(className);

controller = [[c alloc] init];

[self.controllersDict setObject:controller forKey:className];

}

//如果当前已经有视图被显示,则将其取消

if (_mainView.subviews.count 0) {

[[_mainView.subviews firstObject] removeFromSuperview];

}

controller.view.frame = _mainView.frame;

[_mainView addSubview:controller.view];

self.mainController = controller;

}

接着是动画,这里用到的动画主要就是改变视图的大小和位置,用transform即可。获得transform的方法单独抽出来,使用concat将大小变换矩阵和位置变换矩阵连接。接着在动画块中改变主视图的transform即可,当然了,也可以设置上阴影效果等。需要注意的是要根据滑动方向将相应视图调整到最底层。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

CGAffineTransform concat = [self _transformWithMoveDirection:SliderMoveDirectionLeft];

[self.view sendSubviewToBack:_leftView];span style="white-space:pre" /span //将另一个视图调到最下面

[self _configureViewShadowWithDirection:SliderMoveDirectionLeft]; //设置阴影

[UIView animateWithDuration:self.rightOpenDuration

delay:0

usingSpringWithDamping:kOpenSpringDampingspan style="white-space:pre" /span //弹性效果

initialSpringVelocity:kOpenSpringVelocity

options:UIViewAnimationOptionCurveLinear

animations:^{

_mainView.transform = concat;

}

completion:^(BOOL finished) {

_showingLeft = NO;

_showingRight = YES;

self.mainController.view.userInteractionEnabled = NO;

_tapGesture.enabled = YES;

}];

另一方向的雷同

最主要的还是滑动手势操作,也是比较麻烦的地方。不过其实思路比较清晰:获取偏移量,在滑动时计算出对应的变换矩阵并设置,在滑动结束时根据位置与判断点的关系做出相应的动画调整。

例如,滑动过程中向右拉时:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

CGFloat translateX = [recognizer translationInView:_mainView].x;

translateX += currentOffsetX;

float scale = 0;

//向右拉,展示的是左视图

if (translateX 0) {

if (self.canShowLeft == NO || self.leftController == nil) {

return;

}

//将右视图放到底部以将左视图显示出来

[self.view sendSubviewToBack:_rightView];

[self _configureViewShadowWithDirection:SliderMoveDirectionRight];

if (_mainView.frame.origin.x self.leftOffsetX) {

scale = 1 - (_mainView.frame.origin.x / self.leftOffsetX) * (1 - self.leftScale);

} else {

scale = self.leftScale;

}

} else if (translateX 0) {……}

比较头痛的十scale的计算。这里的要求是当view从最初到最末时scale的变化为1.0到self.leftScale,因此利用数学知识推出这个公式即可。上述代码省略了向左拉的代码。

而在拉动结束状态则与左拉右拉动画实现类似。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

CGFloat translateX = [recognizer translationInView:_mainView].x;

translateX += currentOffsetX;

if (translateX self.leftJudgeX) {

if (self.canShowLeft == NO || self.leftController == nil) {

return;

}

CGAffineTransform trans = [self _transformWithMoveDirection:SliderMoveDirectionRight];

[UIView beginAnimations:nil context:nil];

_mainView.transform = trans;

[UIView commitAnimations];

_showingLeft = YES;

_showingRight = NO;

self.mainController.view.userInteractionEnabled = NO;

_tapGesture.enabled = YES;

} else if (translateX -self.rightJudgeX) {……}

ios开发侧拉菜单点击手势怎么实现

你是想要只能从屏幕的左边边缘处向右滑动才有效么?如果是的话,加一个边缘手势就可以解决!如下:

- (void)viewDidLoad {

[super viewDidLoad];

UIScreenEdgePanGestureRecognizer* screenEdgePan = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(action:)];

screenEdgePan.edges = UIRectEdgeLeft;

[self.view addGestureRecognizer:screenEdgePan];

}

-(void)action:(UIScreenEdgePanGestureRecognizer*)sender{

if (sender.edges == UIRectEdgeLeft) {

NSLog(@"正在从左边滑动");

switch (sender.state) {

case UIGestureRecognizerStateBegan:

NSLog(@"手势开始");

break;

case UIGestureRecognizerStateChanged:

NSLog(@"手势进行中");

break;

case UIGestureRecognizerStateEnded:

NSLog(@"手势结束");

break;

default:

break;

}

}

}


网站名称:ios开发侧滑菜单,安卓开发侧滑菜单
当前URL:http://cdkjz.cn/article/hoesdh.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

大客户专线   成都:13518219792   座机:028-86922220