实现侧边抽屉效果-YRSideViewController

滑令
2023-12-01

在项目当中经常用到类似抽屉效果的页面转换,下面是简单的视图切换。

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传到控制器中即可。

转载于:https://www.cnblogs.com/hansIOS/p/5206672.html

 类似资料: