之前写过了一篇关于 导航栏平滑过渡的实现 的文章,主要实现的功能是在控制器转场的过程当中为导航栏实现平滑过渡的效果?;氐娇刂破髯”旧砝此?,它还存在着以下几个痛点:
想通过手势就可以轻松搞定向左或是向右滑动的 push 操作。
想通过手势就可以完成相反方向的 pop 操作。
可以灵活的控制转场的功能。
源码结构
根据上述的几个点实现了一个简单的开源框架: XANavBarTransition 。在原有导航栏平滑过渡的基础上,加上了一些转场方面的功能并且最大程度的降低了对程序的入侵。下面来简单讨论一下设计的思路。
设计思路
在设计之初,我想是通过全局的方式来控制导航控制器的转场,后来在实现的过程中发现这种方式并不能让我很好的去管理控制器与控制器之间的转场调度。这也是我在做 XANavBarTransition 框架时遇到的一个比较难处理的问题。最后拍板:将导航控制器栈中每一个控制器的交互转场配合导航栏平滑过渡作为独立的转场方案(控制器管理着转场会话的创建以及销毁),导航控制器仍然控制并且管理全局性的属性和方法。
上面提到的转场方案其实就是在控制器创建的时候用户要选择是进行向左还是向右的 push 操作,无论是哪种方式,对应相反的方向就是 pop 操作。所有在控制器栈里的控制器皆是如此。同时还要确定定制化的功能,比如:某个页面我想有 push 功能、某个页面我只想要有 pop 功能等等。
push 与 pop 的操作都是由内部的具体的转场对象来进行管理的,该对象会随着控制器的显示而创建,消失而销毁。所以它在每次创建的时候会根据转场方案来进行相应的子类化。
在最开始的时候是想通过系统自带的 pop 功能来完成,后来发现对其控制力不足。push 与 pop 具体的实现思路其实很简单都是通过对控制器添加手势,根据转场的方案和滑动方向来确定是 push 操作还是 pop 操作,并驱动相应的交互式的转场动画。
下面是一段手势识别的代码:
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer{
if(gestureRecognizer == self.interactivePan){
CGPoint point = [gestureRecognizer translationInView:nil];
CGPoint velocity = [gestureRecognizer velocityInView:nil];
if (fabs(velocity.y) > fabs(velocity.x)) {//垂直方向不处理
return NO;
}
if(self.nc.xa_isTransitioning){
return NO;
}
if([self getPushCondition:point]){//push
self.nextVC = [self.transitionDelegate xa_nextViewControllerInTransitionMode:self.transitionMode];//是否为有效的push控制器
if(self.nextVC == nil ||
[self.nc.childViewControllers containsObject:self.nextVC]){
return NO;
}
return self.pushTransitionEnable;
}else if([self getPopCondition:point]){//pop
if(self.nc.viewControllers.count <= 1){//栈底控制器不处理
return NO;
}
return self.popTransitionEnable;
}
return NO;
}
return YES;
}
状态控制
XANavBarTransition 框架中主要有以下几个状态的控制。
每一个控制器都拥有能否 pop 的能力,这个功能会在手势识别阶段完成。
可以控制当前导航控制器下的所有控制器的转场功能。这个功能会在创建转场会话和设置属性的阶段完成。
代理回调: XANavigationControllerObserver 对象负责监听 <UINavigationControllerDelegate> 代理方法。如果App外部有需要监听,可以将实现了 <UINavigationControllerDelegate> 协议的代理设置到导航控制器的 xa_delegate属性当中,内部会做转递。