在项目当中经常用到类似抽屉效果的页面转换,下面是简单的视图切换。
1,首先声明SideViewController,用来装所有要在屏幕中显示的控制器。
2,为SideViewController添加属性和方法,每个属性的作用都有注释。代码如下:
// SliderViewController.h
//普通动画的block
typedef void(^RootViewMoveBlock) (UIView *rootView,CGRect orhinFrame,CGFloat xoffeset);
@interface SideViewController : UIViewController
@property (assign ,nonatomic) BOOL needSwipeShowMenu;//是否开启手势滑动出菜单
@property (strong ,nonatomic) UIViewController *rightViewContorller;//右边控制器
@property (strong,nonatomic) UIViewController *leftViewController;//左边控制器
@property (strong,nonatomic) UIViewController *mainViewController;//主页面控制器
@property (assign,nonatomic) CGFloat leftViewShowWidth;//左侧栏展示大小
@property (assign,nonatomic)CGFloat rightViewShowWidth;//右侧栏展示大小
@property (assign,nonatomic)NSTimeInterval animationDuration;//动画时长
@property (assign,nonatomic)BOOL showBoundsShadow;//是否显示边框阴影
@property (copy,nonatomic)RootViewMoveBlock rootViewMoveBlock;//可在此block做动画效果
//block 的setter方法
-(void)setRootViewMoveBlock:(RootViewMoveBlock)rootViewMoveBlock;
-(void)showLeftViewController:(BOOL)animated;//展示左边栏
-(void)showRightViewController:(BOOL)animated;//展示右边栏
-(void)hideSideViewController:(BOOL)animated;//回复正常位置
@end
可以根据实际情况确定是否使用leftViewController/rightViewController,但是一般情况下我们必须设置mainViewcontroller。
在.h文件中,只是提供了外部接口,让使用起来方便一些。在整个api中,侧边抽屉效果最难的应该就是对视图的缩放处理了,结合起来动画效果,工程量也是不堪小视的。
下面来看源代码:
#import "SideViewController.h"
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
@interface SideViewController ()
{
UIView *_baseView;
UIView *_currentView;//rootViewController.view
UIPanGestureRecognizer *_panGestureRecognizer;
CGPoint _startPanPoint;
CGPoint _lastPanPoint;
BOOL _panMovingRightOrLeft;//true是右,false是左
UIButton *_coverButton;
}
@end
@implementation SideViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_leftViewShowWidth = 267;
_rightViewShowWidth = 267;
_animationDuration = 0.35;
_showBoundsShadow = true;
_panGestureRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
_panMovingRightOrLeft = false;
_lastPanPoint = CGPointZero;
_coverButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
[_coverButton addTarget:self action:@selector(hideSideViewController) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(id)init{
return [self initWithNibName:nil bundle:nil];
}
#pragma mark - 视图进出方法
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_baseView = self.view;
_baseView.backgroundColor = [UIColor orangeColor];
self.needSwipeShowMenu = true;
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
if (!self.mainViewController) {
NSAssert(false, @"还没有设置mainViewCOntroller");
}
if (_currentView != _mainViewController.view) {
[_currentView removeFromSuperview];
_currentView = _mainViewController.view;
[_baseView addSubview:_currentView];
_currentView.frame = _baseView.bounds;
}
}
#pragma mark - setter方法的重写
-(void)setLeftViewController:(UIViewController *)leftViewController{
if (_leftViewController != leftViewController) {
if (_leftViewController) {
[_leftViewController removeFromParentViewController];
}
_leftViewController = leftViewController;
if (_leftViewController) {
[self addChildViewController:_leftViewController];
}
}
}
/**
* rightViewController
*/
-(void)setRightViewContorller:(UIViewController *)rightViewContorller{
if (_rightViewContorller != rightViewContorller) {
if (_rightViewContorller) {
[_rightViewContorller removeFromParentViewController];
}
_rightViewContorller = rightViewContorller;
if (_rightViewContorller) {
[self addChildViewController:_rightViewContorller];
}
}
}
/**
* needSwipeShowMenu
*/
-(void)setNeedSwipeShowMenu:(BOOL)needSwipeShowMenu{
_needSwipeShowMenu = needSwipeShowMenu;
if (needSwipeShowMenu) {
[_baseView addGestureRecognizer:_panGestureRecognizer];
}else{
[_baseView removeGestureRecognizer:_panGestureRecognizer];
}
}
-(void)showShadow:(BOOL)show{
_currentView.layer.shadowOpacity = show? .8f : .0f;
if (show) {
_currentView.layer.cornerRadius = 4.0f;
_currentView.layer.shadowOffset = CGSizeZero;
_currentView.layer.shadowRadius = 4.0f;
_currentView.layer.shadowPath = [UIBezierPath bezierPathWithRect:_currentView.bounds].CGPath;
}
}
#pragma mark - showOrHideTheView
-(void)willShowLeftViewController{
if (!_leftViewController || _leftViewController.view.superview) {
return;
}
_leftViewController.view.frame = _baseView.bounds;
[_baseView insertSubview:_leftViewController.view belowSubview:_currentView];
if (_rightViewContorller && _rightViewContorller.view.superview) {
[_rightViewContorller.view removeFromSuperview];
}
}
-(void)willShowRightViewController{
if (!_rightViewContorller || _rightViewContorller.view.superview) {
return;
}
_rightViewContorller.view.frame=_baseView.bounds;
[_baseView insertSubview:_rightViewContorller.view belowSubview:_currentView];
if (_leftViewController && _leftViewController.view.superview) {
[_leftViewController.view removeFromSuperview];
}
}
-(void)showLeftViewController:(BOOL)animated{
if (!_leftViewController) {
return;
}
[self willShowLeftViewController];
NSTimeInterval animatedTime = 0;
if (animated) {
animatedTime = ABS(_leftViewShowWidth - _currentView.frame.origin.x) / _leftViewShowWidth * _animationDuration;
}
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView animateWithDuration:animatedTime animations:^{
[self layoutCurrentViewWithOffset:_leftViewShowWidth];
[_currentView addSubview:_coverButton];
[self showShadow:_showBoundsShadow];
}];
}
-(void)showRightViewController:(BOOL)animated{
if (!_rightViewContorller) {
return;
}
[self willShowRightViewController];
NSTimeInterval animatedTime = 0;
if (animated) {
animatedTime = ABS(_rightViewShowWidth + _currentView.frame.origin.x) / _rightViewShowWidth * _animationDuration;
}
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView animateWithDuration:animatedTime animations:^{
[self layoutCurrentViewWithOffset:-_rightViewShowWidth];
[_currentView addSubview:_coverButton];
[self showShadow:_showBoundsShadow];
}];
}
-(void)hideSideViewController:(BOOL)animated{
[self showShadow:false];
NSTimeInterval animatedTime = 0;
if (animated) {
animatedTime = ABS(_currentView.frame.origin.x / (_currentView.frame.origin.x > 0 ? _leftViewShowWidth:_rightViewShowWidth))*_animationDuration;
}
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView animateWithDuration:animatedTime animations:^{
[self layoutCurrentViewWithOffset:0];
} completion:^(BOOL finished) {
[_coverButton removeFromSuperview];
[_leftViewController.view removeFromSuperview];
[_rightViewContorller.view removeFromSuperview];
}];
}
-(void)hideSideViewController{
[self hideSideViewController:true];
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
// Check for horizontal pan gesture
if (gestureRecognizer == _panGestureRecognizer) {
UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer*)gestureRecognizer;
CGPoint translation = [panGesture translationInView:_baseView];
if ([panGesture velocityInView:_baseView].x < 600 && ABS(translation.x)/ABS(translation.y)>1) {
return YES;
}
return NO;
}
return YES;
}
- (void)pan:(UIPanGestureRecognizer*)pan{
if (_panGestureRecognizer.state==UIGestureRecognizerStateBegan) {
_startPanPoint=_currentView.frame.origin;
if (_currentView.frame.origin.x==0) {
[self showShadow:_showBoundsShadow];
}
CGPoint velocity=[pan velocityInView:_baseView];
if(velocity.x>0){
if (_currentView.frame.origin.x>=0 && _leftViewController && !_leftViewController.view.superview) {
[self willShowLeftViewController];
}
}else if (velocity.x<0) {
if (_currentView.frame.origin.x<=0 && _rightViewContorller && !_rightViewContorller.view.superview) {
[self willShowRightViewController];
}
}
return;
}
CGPoint currentPostion = [pan translationInView:_baseView];
CGFloat xoffset = _startPanPoint.x + currentPostion.x;
if (xoffset>0) {//向右滑
if (_leftViewController && _leftViewController.view.superview) {
xoffset = xoffset>_leftViewShowWidth?_leftViewShowWidth:xoffset;
}else{
xoffset = 0;
}
}else if(xoffset<0){//向左滑
if (_rightViewContorller && _rightViewContorller.view.superview) {
xoffset = xoffset<-_rightViewShowWidth?-_rightViewShowWidth:xoffset;
}else{
xoffset = 0;
}
}
if (xoffset!=_currentView.frame.origin.x) {
[self layoutCurrentViewWithOffset:xoffset];
}
if (_panGestureRecognizer.state==UIGestureRecognizerStateEnded) {
if (_currentView.frame.origin.x!=0 && _currentView.frame.origin.x!=_leftViewShowWidth && _currentView.frame.origin.x!=-_rightViewShowWidth) {
if (_panMovingRightOrLeft && _currentView.frame.origin.x>20) {
[self showLeftViewController:true];
}else if(!_panMovingRightOrLeft && _currentView.frame.origin.x<-20){
[self showRightViewController:true];
}else{
[self hideSideViewController];
}
}else if (_currentView.frame.origin.x==0) {
[self showShadow:false];
}
_lastPanPoint = CGPointZero;
}else{
CGPoint velocity = [pan velocityInView:_baseView];
if (velocity.x>0) {
_panMovingRightOrLeft = true;
}else if(velocity.x<0){
_panMovingRightOrLeft = false;
}
}
}
//重写此方法可以改变动画效果,PS._currentView就是RootViewController.view
- (void)layoutCurrentViewWithOffset:(CGFloat)xoffset{
if (_showBoundsShadow) {
_currentView.layer.shadowPath = [UIBezierPath bezierPathWithRect:_currentView.bounds].CGPath;
}
if (self.rootViewMoveBlock) {//如果有自定义动画,使用自定义的效果
self.rootViewMoveBlock(_currentView,_baseView.bounds,xoffset);
return;
}
/*平移的动画
[_currentView setFrame:CGRectMake(xoffset, _baseView.bounds.origin.y, _baseView.frame.size.width, _baseView.frame.size.height)];
return;
//*/
// /*平移带缩放效果的动画
static CGFloat h2w = 0;
if (h2w==0) {
h2w = _baseView.frame.size.height/_baseView.frame.size.width;
}
CGFloat scale = ABS(600 - ABS(xoffset)) / 600;
scale = MAX(0.8, scale);
_currentView.transform = CGAffineTransformMakeScale(scale, scale);
CGFloat totalWidth=_baseView.frame.size.width;
CGFloat totalHeight=_baseView.frame.size.height;
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
totalHeight=_baseView.frame.size.width;
totalWidth=_baseView.frame.size.height;
}
if (xoffset>0) {//向右滑的
[_currentView setFrame:CGRectMake(xoffset, _baseView.bounds.origin.y + (totalHeight * (1 - scale) / 2), totalWidth * scale, totalHeight * scale)];
}else{//向左滑的
[_currentView setFrame:CGRectMake(_baseView.frame.size.width * (1 - scale) + xoffset, _baseView.bounds.origin.y + (totalHeight*(1 - scale) / 2), totalWidth * scale, totalHeight * scale)];
}
//*/
}
@end
整个.m文件是对setter方法的重写和一些动画效果,以及缩放效果的实现。
整个缩放效果的逻辑都在layoutCurrentViewWithOffset:xoffset中,在左右视图出现的时候都会调用这个方法,对控制器进行图形处理。
如果我们想使用普通的平移效果,那么只需要在用到sideViewController的地方实现回调的rootViewBlock即可:
[sideViewController setRootViewMoveBlock:^(UIView *rootView, CGRect orginFrame, CGFloat xoffset) {
//使用简单的平移动画
rootView.frame=CGRectMake(xoffset, orginFrame.origin.y, orginFrame.size.width, orginFrame.size.height);
}];
假设我们已经在其他控制器或者appdelegate里面设置了sideViewcontroller左右控制器,那么,我们可以用单例来取到:(appdelegate.h里面有sideViewController属性)
AppDelegate *delegate=(AppDelegate*)[[UIApplication sharedApplication]delegate];
SideViewController *sideViewController=[delegate sideViewController];
[sideViewController showLeftViewController:true];//通过他来设置左右滑动。
左右控制器的事件我们也可以在当前使用sideViewController中实现,只需要在左右控制器中写好回调的block或者使用delegate传到控制器中即可。