=。=原来的工程忘记点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调用的响应事件,所以这里只要让自定义的手势调用就行了。