[Note] CYLTabBarController

作者: _lemon | 来源:发表于2016-01-03 21:20 被阅读7198次

“如果一个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,对外的接口就是两个需要设置的数组viewControllerstabBarItemsAttributes

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,设置了它的行为。

相关文章

网友评论

本文标题:[Note] CYLTabBarController

本文链接:https://www.haomeiwen.com/subject/xkrohttx.html