标签:
这篇文章大致会带你实现以下的功能,废话少说,先看东西:
02、此次新增加自定义TabBar
现有问题
- 某些同学做购物软件,可能需要在某个控制器屏幕最下方一直悬浮一个“立即购买”、“马上咨询”之类的控件。有经验的同学遇到此类功能时可能会采用新建一个Window,然后再把自己需要的控件添加到这个Window上,然后设置这个Window的hidden = NO,就可以实现了。确实是这样,其实除了新建一个Window,你还可以将自己的控件添加到当前KeyWindow上,同样可以实现这个功能。
- 这个功能实现了,但是有一点很恶心的是,苹果自从iOS7开始,有了右滑手势这个东西。这个时候,相比于安卓前进后退按钮是单一的在窗口显示一个控制器,右滑手势可以让窗口同时显示两个控制器的View。
- 如果你按照上面添加Window或是添加在KeyWindow上的方式。第一、你就不能让你定义的这个控件随着用户右滑而右滑。第二,你只能在这两个方法里控制你的控件的显示和隐藏。但是试过以后你会发现,当用户右滑的时候,你的控件一直在跳跃闪烁。但是我们是有追求的程序狗,我们显然会对这种方案SAY:FUCK AWAY!THIS IS SO UGLY MAN. AND IT SMELLS LIKE SHIT.
-(void)viewWillAppear:(BOOL)animated -(void)viewWillDisappear:(BOOL)animated
解决方案
- 此次我结合之前的自定义导航栏,给出了一个比较“优雅”的解决方案:
- 假如A是根控制器,B是下一个要Push过去的控制器,你只需要在A里Push方法后写如下一行。你就可以拥有一个和导航栏一样跟随用户右滑而流畅滑动的“联动”TabBar了,并且你不用关心这个TabBar 的显示和隐藏。
[self.navigationController pushViewController:YourOwnVC animated:YES]; YourOwnVC.navigationController.jp_linkViewHeight = YourHopeHeight;
看到我这篇文章的同学99%都是没有看过我之前那篇文章的,所以我有必要再重新描述下之前文章的内容。但是如果你要详细了解实现思路还是需要回头看我那篇文章,因为这篇文章我是讲TabBar的,所以NavgationController不会讲的很细。
之前的文章大致说清楚了,怎么样去适应需要为每个界面都定制导航条的需求。
实现思路如下:
[[JPNavigationController alloc]initWithRootViewController:vc];
实现目标:1.临时关闭Pop手势 2.自定义右滑手势有效区域
系统的interactivePopGestureRecognizer只提供了一个enabled的属性给我们,所以如果想要做更多的事就要想其他的办法。在这种情况下,我选择了自定义一个UIPanGestureRecognizer,替换系统的手势的实现
// 自定义的pop手势
-(UIPanGestureRecognizer *)jp_fullscreenPopGestureRecognizer{
if (!_jp_fullscreenPopGestureRecognizer) {
_jp_fullscreenPopGestureRecognizer = [UIPanGestureRecognizer new];
_jp_fullscreenPopGestureRecognizer.maximumNumberOfTouches = 1;
}
return _jp_fullscreenPopGestureRecognizer;
}
-(void)viewDidLoad{
[super viewDidLoad];
// 彻底隐藏导航栏
[self setNavigationBarHidden:YES];
// 添加pop手势(懒加载)
if (![self.interactivePopGestureRecognizer.view.gestureRecognizers containsObject:self.jp_fullscreenPopGestureRecognizer]) {
[self.interactivePopGestureRecognizer.view addGestureRecognizer:self.jp_fullscreenPopGestureRecognizer];
// 用自己的手势替换系统的pop
NSArray *targets = [self.interactivePopGestureRecognizer valueForKey:@"targets"];
id target = [targets.firstObject valueForKey:@"target"];
SEL action = NSSelectorFromString(@"handleNavigationTransition:");
self.jp_fullscreenPopGestureRecognizer.delegate = [self jp_popGestureRecognizerDelegate];
[self.jp_fullscreenPopGestureRecognizer addTarget:target action:action];
// 系统手势置为不可用
self.interactivePopGestureRecognizer.enabled = NO;
}
}
这样我们用自己的手势替代了系统的右滑功能,所以我们可以在自定义的手势代理方法中自定义右滑的特性:
-(BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer{
// 根控制器不允许pop
if (self.navigationController.viewControllers.count <= 1) {
return NO;
}
// 当用户禁止的时候,不允许pop
if (!self.navigationController.jp_fullScreenPopGestureEnabled) {
return NO;
}
// 当开始触发的点大于用户指定的最大触发点的时候,禁止pop
CGPoint beginningLocation = [gestureRecognizer locationInView:gestureRecognizer.view];
CGFloat maxAllowedInitialDistance = self.navigationController.jp_interactivePopMaxAllowedInitialDistanceToLeftEdge;
if (maxAllowedInitialDistance >= 0 && beginningLocation.x > maxAllowedInitialDistance) {
return NO;
}
// 正在做过渡动画的时候禁止pop
if ([[self.navigationController valueForKey:@"_isTransitioning"] boolValue]) {
return NO;
}
// 反向滑动禁止pop
CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
if (translation.x <= 0) {
return NO;
}
// 暂时关闭pop手势禁止pop
if (self.closePopForTemporary) {
return NO;
}
return YES;
}
JPNavigationBar *navBar = [[JPNavigationBar alloc]init];
[self setValue:navBar forKey:@"navigationBar"];
现在我们自定义了导航条,我们就可以在JPNavigationBar中重载hitTest方法,在这个方法中遍历它自身的子控件,找到我们添加的JPLinkContainerView。当点击的点在我们添加的占位容器视图JPLinkContainerView身上的时候,我们就手动把点击事件分发给我们自定义的TabBar子控件上,完成响应者链条的事件传递。
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
JPLinkContainerView *linkView;
for (UIView *subview in self.subviews) {
if ([subview isKindOfClass:[JPLinkContainerView class]]) {
linkView = (JPLinkContainerView *)subview;
break;
}
}
CGPoint viewP = [self convertPoint:point toView:linkView];
if ([linkView pointInside:viewP withEvent:event]) { // 如果点击的点在联动视图linkView上, 就由linkView来响应事件
return [linkView hitTest:viewP withEvent:event];
}
else{
return [super hitTest:point withEvent:event]; // 否则, 执行系统默认的做法
}
}
@property(nonatomic)CGFloat jp_linkViewHeight;
使用时注意 : 如果你下个界面需要有联动底部视图, 你在上个控制器 - (void)pushViewController:animated:方法后面立即把值传给我:
[self.navigationController pushViewController:YourOwnVC animated:YES];
YourOwnVC.navigationController.jp_linkViewHeight = YourHopeHeight;
同时注意 : 这两行代码有逻辑关系,必须先调用push方法,navigationController才会alloc,分配内存地址,才有值。
@property(nonatomic)JPLinkContainerView *jp_linkContainerView;
需要添加自定义的视图的时候,只要把自定义的视图添加到这个视图上就可以了
注意 : 如果识别到你当前控制器为UITableViewController的时候, 如果有联动底部视图, 就会自动为你添加jp_linkViewHeight高度的底部额外滚动区域。 但是, 如果你的控制器是UIViewController上添加了UITableView, 那我不会自动为你添加底部额外滚动区域, 需要你自己为UITableView添加contentInset。
框架的Github地址在这里[JPNavigationController]。如果我的文章在你实际工作中恰好帮到了你,麻烦你给个小星星,谢谢。更有甚,如果你和我一样热爱开源、热爱分享,或许可以小手一抖,帮我转发给更多朋友看到。
1行代码为每个Controller自定义“TabBar”-b
标签:
原文地址:http://www.cnblogs.com/isItOk/p/5755051.html