热门搜索 :
考研考公
您的当前位置:首页正文

ios QQ抽屉框架以及返回手势

来源:东饰资讯网
效果图.gif

=。=原来的工程忘记点commit所以没有提交成功,时隔三月才想起。16/10/4

重新发一波github地址:

-------------手打的分割线----------------------------------------------

抽屉的实现原理:抽屉控制器中包含:一个主view,一侧拉栏view,主view在侧拉篮的上面。给抽屉控制器添加一个滑动手势,通过滑动的偏移量来设置主view和侧拉栏的相对位置:

#pragma mark - 滑动手势

//滑动手势
- (void) handlePan: (MyPanGestureRecognizer *)rec{
    
    //起始位置判断,只能抽屏幕边沿起始
    CGPoint beginPoint = rec.beginPoint;
    if (beginPoint.x > 15 && self.closed) {
        return;
    }
    
    CGPoint point = [rec translationInView:self.view];
    _scalef = (point.x * self.speedf + _scalef);
    
    
    //判断是否是向右上滑动
    BOOL needMoveWithTap = YES;  //是否还需要跟随手指移动
        if (((self.mainVC.view.x <= 0) && (_scalef <= 0)) || ((self.mainVC.view.x >= (kScreenWidth - kMainPageDistance )) && (_scalef >= 0)))
        {
            //边界值管控
            _scalef = 0;
            needMoveWithTap = NO;
        }
        
        //根据视图位置判断是左滑还是右边滑动
        if (needMoveWithTap && (rec.view.frame.origin.x >= 0) && (rec.view.frame.origin.x <= (kScreenWidth - kMainPageDistance)))
        {
            CGFloat recCenterX = rec.view.center.x + point.x * self.speedf;
            
            if (recCenterX < kScreenWidth * 0.5 - 2) {
                recCenterX = kScreenWidth * 0.5;
            }
            
            CGFloat recCenterY = rec.view.center.y;
            
            rec.view.center = CGPointMake(recCenterX,recCenterY);
            
            //scale 1.0~kMainPageScale
            CGFloat scale = 1 - (1 - kMainPageScale) * (rec.view.frame.origin.x / (kScreenWidth - kMainPageDistance));
            
            rec.view.transform = CGAffineTransformScale(CGAffineTransformIdentity,scale, scale);
            [rec setTranslation:CGPointMake(0, 0) inView:self.view];
            
            CGFloat leftTabCenterX = kLeftCenterX + ((kScreenWidth - kMainPageDistance) * 0.5 - kLeftCenterX) * (rec.view.frame.origin.x / (kScreenWidth - kMainPageDistance));
            
            //        NSLog(@"%f",leftTabCenterX);
            
            
            //leftScale kLeftScale~1.0
            CGFloat leftScale = kLeftScale + (1 - kLeftScale) * (rec.view.frame.origin.x / (kScreenWidth - kMainPageDistance));
            
            self.leftTableview.center = CGPointMake(leftTabCenterX, kScreenHeight * 0.5);
            self.leftTableview.transform = CGAffineTransformScale(CGAffineTransformIdentity, leftScale,leftScale);
            
            //tempAlpha kLeftAlpha~0
            CGFloat tempAlpha = kLeftAlpha - kLeftAlpha * (rec.view.frame.origin.x / (kScreenWidth - kMainPageDistance));
            self.contentView.alpha = tempAlpha;
            
        }
        else
        {
            //超出范围,
            if (self.mainVC.view.x < 0)
            {
                [self closeLeftView];
                _scalef = 0;
            }
            else if (self.mainVC.view.x > (kScreenWidth - kMainPageDistance))
            {
                [self openLeftView];
                _scalef = 0;
            }
        }

    
    
    //手势结束后修正位置,超过约一半时向多出的一半偏移
    if (rec.state == UIGestureRecognizerStateEnded) {
        if (fabs(_scalef) > vCouldChangeDeckStateDistance)
        {
            if (self.closed)
            {
                [self openLeftView];
            }
            else
            {
                [self closeLeftView];
            }
        }
        else
        {
            if (self.closed)
            {
                [self closeLeftView];
            }
            else
            {
                [self openLeftView];
            }
        }
        _scalef = 0;
    }
}

上面是滑动的手势对应的响应view的处理,QQ的滑动手势是从屏幕的边缘开始的时候才会生效,所以需要获取到手势的起始位置,这需要自定义一个滑动手势:

#import <UIKit/UIKit.h>

@interface MyPanGestureRecognizer : UIPanGestureRecognizer
@property(assign , nonatomic)CGPoint beginPoint;
@property(strong , nonatomic) UIView *  inView;

-(instancetype)initWithTarget:(id)target action:(SEL)action inview:(UIView *)view;
@end

#import "MyPanGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
@implementation MyPanGestureRecognizer

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    
    UITouch * touch = [touches anyObject];
    CGPoint point = [touch locationInView:self.inView];
    self.beginPoint = point;
    
}
-(instancetype)initWithTarget:(id)target action:(SEL)action inview:(UIView *)view{
    self = [super initWithTarget:target action:action];
    if (self) {
        self.inView = view;
    }
    return self;
}

@end

在抽屉框架搭建好之后,需要实现在在侧拉栏的点击事件,在主view完成push。大致的流程入下:

#pragma mark -- 抽屉栏页面转跳的通知
/**
 *  通知流程:
 *  1.在MainTabBarViewController,发送切换了tab的通知,通知名:MainTabChanged,内容selectedIndex
 *  2.在LeftTableViewController,中接受通知,接收selectedindex,
 *  3. 抽屉栏点击事件后需要跳转页面,关闭抽屉, 通过HomePage+index+push名通知主页push,内容需要push的vc。
 */
1.
-(void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item{
    int index ;
    if ([item.title isEqualToString:@"消息"])
    {
        index = 0;
    }else if ([item.title isEqualToString:@"联系人"])
    {
        index = 1;
    }else if ( [item.title isEqualToString:@"动态"] )
    {
        index = 2;
    }
    [[NSNotificationCenter defaultCenter] postNotificationName:@"MainTabChanged" object:nil userInfo:@{@"selectedIndex":@(index)}];
}
2.
#pragma make -- MainTabChanged通知事件
-(void)mainTabChanged:(NSNotification *) notification{
    NSDictionary * dict = notification.userInfo;
    self.homePageIndex = [dict[@"selectedIndex"] intValue];
}
3.
#pragma mark -- 点击事件跳转
-(void)notificationHomePagePushViewController:(UIViewController *) viewController{

//[appDelegate.mainTabBarViewController.messageViewController.navigationController pushViewController:otherViewController animated:YES];
 //    3. 抽屉栏点击事件后需要跳转页面,关闭抽屉, 通过HomePage+index+push名通知主页push,内容需要push的vc。
    if (viewController)
    {
        NSString * postName = [NSString stringWithFormat:@"HomePage%dPush",self.homePageIndex];
        [[NSNotificationCenter defaultCenter] postNotificationName:postName object:nil userInfo:@{@"pushViewController":viewController}];
    }
}
4.在主view(tabbarcontroller)的child view中完成push
    //接收抽屉点击事件的HomePage2Push通知
    [[NSNotificationCenter defaultCenter]  addObserver:self selector:@selector(pushViewControllerFromLeftView:) name:@"HomePage2Push" object:nil];
}

#pragma mark -- 抽屉界面转跳
-(void)pushViewControllerFromLeftView:(NSNotification *) notification{
    NSDictionary * dict  = notification.userInfo;
    UIViewController * pushViewController = [dict objectForKey:@"pushViewController"];
    self.hidesBottomBarWhenPushed = YES;
    
    //关闭抽屉
    AppDelegate * appDelegate  =(AppDelegate *) [UIApplication sharedApplication].delegate;
    [appDelegate.leftSliderViewController closeLeftView];
    
    [self.navigationController pushViewController:pushViewController animated:YES];
    
    self.hidesBottomBarWhenPushed = NO;
}

这样就算完成了抽屉部分。之后是返回手势,可以注意qq的返回手势设计是在界面的任意地方向右滑动,就能触发返回事件。而系统自带的只能以屏幕边缘为起点的时候,在产能触发。对于这个问题,网上有个第三框架可以做到,并且对pop的转场动画进行了优化。原理的是一样的,自定义一个手势,然后拦截系统自带返回手势,用自定义的手势触发返回事件:

-(void)setInteractivePopGestureRecognizer{
    //设置滑动
//    self.navigationController.interactivePopGestureRecognizer.delegate = weakSelf;
     UIGestureRecognizer *gesture = self.navigationController.interactivePopGestureRecognizer;
    
    NSMutableArray *_targets = [gesture valueForKey:@"_targets"];
    //获取它的唯一对象 有一个叫UIGestureRecognizerTarget的私有类,有一个属性叫_target
    id gestureRecognizerTarget = [_targets firstObject];
    //获取_target:_UINavigationInteractiveTransition 有一个方法叫handleNavigationTranstion
    id navigationInteractiveTransition = [gestureRecognizerTarget valueForKey:@"_target"];
    //从控制台获取出他的方法签名
    SEL handelTransition = NSSelectorFromString(@"handleNavigationTransition:");
    //创建一个与系统一毛一样的手势 我们只把他的类改为UIPanGestureRecognizer
    UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc]  initWithTarget:navigationInteractiveTransition action:handelTransition];
    
    pan.delegate = self;
    [self.view addGestureRecognizer:pan];
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;

}

handleNavigationTransition:就是interactivePopGestureRecognizer调用的响应事件,所以这里只要让自定义的手势调用就行了。

Top