实现侧边抽屉效果-YRSideViewController
在项目当中经常用到类似抽屉效果的页面转换,下面是简单的视图切换。
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传到控制器中即可。