“如果一个button有一部分超出父控件的范围了,这部分无法响应点击,如果想让它响应点击应该怎么做?”
“这是一道送分题啊同学们!!!” [\债见]
原地址:https://github.com/ChenYilong/CYLTabBarController
只笔记一下核心的类吧,demo部分不写了,因为实在是封装得特别特别特别特别干净。
CYLTabBarControllerConfig
提供一个CYLTabBarController属性,接口做成只读,内部实现改成readwrite。给它配置了两个数组,viewControllers和tabBarItemsAttributes属性。README里提到了,因为用的是原生的控件,所以可以直接用[UITabBar appearance]来设置样式,比如选中时的背景。背景是画上去的,第一个imageContext画颜色,第二个imageContext画圆角。
然后AppDelegate里面完成配置很简单,README里也写了。有一个细节是,设置主窗口的时候调了一个[self customizeInterface]
,这个方法里只做了一件事,调了一下[self setUpNavigationBarAppearance]
。可以想象我肯定会把导航栏的设置方法直接写到设置主窗口的位置_(:з」∠)_…按作者这样写结构就好很多,以后再想添加别的设置外观的方法也会很方便,不会让设置主窗口的地方变得臃肿。
CYLTabBarController
CYLTabBarController这个类直接继承于系统的UITabBarController,对外的接口就是两个需要设置的数组viewControllers
和tabBarItemsAttributes
。
setViewControllers:
addOneChildViewController:WithTitle:normalImageName:selectedImageName:
这两个方法做的是根据viewControllers和tabBarItemsAttributes来添加和设置container里的控制器和tabBarItem。
关于container里的控制器,从父视图移除的写法:
[vc willMoveToParentViewController:nil];
[vc.view removeFromSuperview];
[vc removeFromParentViewController];
// [vc didMoveToParentViewController:nil] called automatically
添加到父视图的写法:
[self addChildViewController:vc];
// [vc willMoveToParentViewController:self] called automatically
[self.view addSubview:vc.view]; // or something like this.
[vc didMoveToParentViewController:self];
这里的写法是有顺序的。第一个场景在setViewControllers:
里见到了,不过在demo里一开始的这个if好像从来就没进去过…因为setViewControllers只调了一次吧,如果调第二次重新设置viewControllers,原有的被添加的控制器和视图就需要都被移除了。第二个场景的写法没有见到,因为这里只是设置一下view controller array的setter,添加到父视图我猜应该是在切换视图的时候调用,这里直接继承于UITabBarController,这个实现就不用自己写了。
这里还写了两个分类,一个内部的分类UIViewController(CYLTabBarControllerItemInternal)
,一个对外的分类UIViewController(CYLTabBarController)
,提供了一个属性cyl_tabBarControleller
,注释里写了这是返回的TabBarController里包含的最顶层的view controller。因为TabBarController是一个container,里面装的控制器可以有很复杂的结构,这里找的是最顶层的那个控制器,比如在这个demo里就是navigation controller。做法是通过associated object(第一次读到活的associated object,一颗赛艇!),就是在把view controller array里面的控制器添加到TabBarController的时候,直接把它们都关联上tabBarController,然后需要返回的时候,通过objc_getAssociatedObject
和parentViewController一层层往上找,直到找到当初被关联的那个控制器。demo里好像并没有调这个方法,应该是便于以后某些场景用吧。
CYLTabBar
hitTest:withEvent:
先看这个面试时遇到的方法好了。这里event发生在TabBar上,point是采取TabBar的坐标,点击plusButton凸出的部分,可以看到point的y是负值,也就是超出TabBar区域了,调用UIView *result = [super hitTest:point withEvent:event];
得到result = nil
。要使event能响应的话,就得使point的值落在响应范围内,就要把坐标换到plusButton的坐标系,然后再调用hitTest:withEvent:
,返回的result就是plusButton了。这里采用reverseObjectEnumerator我猜是因为plusButton是最后一个subview,这样返回速度会快一点?但是点击别的TabBarItem好像还是要轮一遍…所以我也不是特别懂是不是真的是这样优化的原因...
我想了一下如果像以下这样写会不会快一点呢我不是很确定...(嗯这个地方已经询问过作者,提了个pr然后merge啦(๑• . •๑) )
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (!self.clipsToBounds && !self.hidden && self.alpha > 0) {
UIView *result = [super hitTest:point withEvent:event];
if (result) {
return result;
}
else {
for (UIView *subview in self.subviews.reverseObjectEnumerator) {
CGPoint subPoint = [subview convertPoint:point fromView:self];
result = [subview hitTest:subPoint withEvent:event];
if (result) {
return result;
}
}
}
}
return nil;
}
layoutSubviews
这个方法比较长,不过不是很复杂,做的是调整plusButton和之后TabBarItem的位置。plusButton默认的是使底部贴在TabBar底部这样来计算位置(如果没有TabBar高就直接放中间)。只是修改后面TabBarButton位置的时候,x值和width值是分别做的修改,我改成一起修改好像没看出什么区别,不太懂这里为什么要写成分别修改呢....
CYLPlusButton, CYLPlusButtonSubclass
这两个类不太复杂,虽然我也是因为太弱了第一次见这种registerSubclass
的用法…感觉CYLPlusButton存在的意义主要是为了设置了一个protocol以及提供了一个extern UIButton对象,嗯在之前的类里也多次用到了这个唯一的对象。然后CYLPlusButtonSubclass是继承于CYLPlusButton,通过实现协议方法还有一些其他方法,定制了一个plusButton,设置了它的行为。
网友评论
@import Foundation;
@import UIKit;
@interface BaseNavigationController : UINavigationController
@EnD
@Implementation BaseNavigationController
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (self.viewControllers.count > 0) {
viewController.hidesBottomBarWhenPushed = YES;
}
[super pushViewController:viewController animated:animated];
}
@EnD
你好,demo里在NSObject 里最上面 加了这么一段,对于小白来说,很难理解,可以解释一下吗?
https://github.com/ChenYilong/CYLTabBarController#%E8%AE%BF%E9%97%AE%E5%88%9D%E5%A7%8B%E5%8C%96%E5%A5%BD%E7%9A%84-cyltabbarcontroller-%E5%AF%B9%E8%B1%A1