iOS开发文章iOSiOS开发指南

【低耦合集成TabBarController】最低只需传两个数组

2015-10-26  本文已影响2722人  iOS程序犭袁

CYLTabBarController【低耦合集成TabBarController】

<p align="center">


bitHound
enter image description here 5 允许指定加号按钮位置 效果如下:</p> enter image description here </p>Airbnb-app效果:</p> enter image description here 6 支持让 TabBarItem 仅显示图标,并自动使图标垂直居中,支持自定义TabBar高度 效果可见Airbnb-app效果,或者下图</p> enter image description here enter image description here

支持横竖屏


enter image description here
本仓库配套Demo的效果: 另一个Demo 使用CYLTabBarController实现了微博Tabbar框架,效果如下
enter image description here enter image description here

项目结构

enter image description here

做下说明:


├── CYLTabBarController  #核心库文件夹,如果不使用 CocoaPods 集成,请直接将这个文件夹拖拽带你的项目中
└── Example
   └── Classes
       ├── Module       #模块类文件夹
       │   ├── Home
       │   ├── Message
       │   ├── Mine
       │   └── SameCity
       └── View         #这里放着 CYLPlusButton 的子类 CYLPlusButtonSubclass,演示了如何创建自定义的形状不规则加号按钮
       
       

使用CYLTabBarController

四步完成主流App框架搭建:

  1. 第一步:使用CocoaPods导入CYLTabBarController
  2. 第二步:设置CYLTabBarController的两个数组:控制器数组和TabBar属性数组
  3. 第三步:将CYLTabBarController设置为window的RootViewController
  4. 第四步(可选):创建自定义的形状不规则加号按钮

第一步:使用CocoaPods导入CYLTabBarController

Podfile 中进行如下导入:

pod 'CYLTabBarController'

然后使用 cocoaPods 进行安装:

如果尚未安装 CocoaPods, 运行以下命令进行安装:

gem install cocoapods

安装成功后就可以安装依赖了:

建议使用如下方式:

# 禁止升级CocoaPods的spec仓库,否则会卡在 Analyzing dependencies ,非常慢 
pod update --verbose --no-repo-update

如果提示找不到库,则可去掉 --no-repo-update

pod update

第二步:设置CYLTabBarController的两个数组:控制器数组和TabBar属性数组

- (void)setupViewControllers {
   CYLHomeViewController *firstViewController = [[CYLHomeViewController alloc] init];
   UIViewController *firstNavigationController = [[UINavigationController alloc]
                                                  initWithRootViewController:firstViewController];
   
   CYLSameFityViewController *secondViewController = [[CYLSameFityViewController alloc] init];
   UIViewController *secondNavigationController = [[UINavigationController alloc]
                                                   initWithRootViewController:secondViewController];
   

   CYLTabBarController *tabBarController = [[CYLTabBarController alloc] init];
   [self customizeTabBarForController:tabBarController];
   
   [tabBarController setViewControllers:@[
                                          firstNavigationController,
                                          secondNavigationController,
                                          ]];
   self.tabBarController = tabBarController;
}

/*
*
在`-setViewControllers:`之前设置TabBar的属性,
*
*/
- (void)customizeTabBarForController:(CYLTabBarController *)tabBarController {
   
   NSDictionary *dict1 = @{
                           CYLTabBarItemTitle : @"首页",
                           CYLTabBarItemImage : @"home_normal",
                           CYLTabBarItemSelectedImage : @"home_highlight",
                           };
   NSDictionary *dict2 = @{
                           CYLTabBarItemTitle : @"同城",
                           CYLTabBarItemImage : @"mycity_normal",
                           CYLTabBarItemSelectedImage : @"mycity_highlight",
                           };

   NSArray *tabBarItemsAttributes = @[ dict1, dict2 ];
   tabBarController.tabBarItemsAttributes = tabBarItemsAttributes;
}

第三步:将CYLTabBarController设置为window的RootViewController

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/* *省略部分:   * */
   [self.window setRootViewController:self.tabBarController];
/* *省略部分:   * */
   return YES;
}

第四步(可选):创建自定义的形状不规则加号按钮

创建一个继承于 CYLPlusButton 的类,要求和步骤:

  1. 实现 CYLPlusButtonSubclassing 协议

  2. 子类将自身类型进行注册,一般可在 applicationapplicationDelegate 方法里面调用 [YourClass registerSubClass] 或者在子类的 +load 方法中调用:

+(void)load {
   [super registerSubclass];
}

协议提供了可选方法:

+ (NSUInteger)indexOfPlusButtonInTabBar;
+ (CGFloat)multiplerInCenterY;
+ (UIViewController *)plusChildViewController;

作用分别是:

+ (NSUInteger)indexOfPlusButtonInTabBar;

用来自定义加号按钮的位置,如果不实现默认居中,但是如果 tabbar 的个数是奇数则必须实现该方法,否则 CYLTabBarController 会抛出 exception 来进行提示。

主要适用于如下情景:

enter image description here

Airbnb-app效果:

enter image description here
+ (CGFloat)multiplerInCenterY;

该方法是为了调整自定义按钮中心点Y轴方向的位置,建议在按钮超出了 tabbar 的边界时实现该方法。返回值是自定义按钮中心点Y轴方向的坐标除以 tabbar 的高度,如果不实现,会自动进行比对,预设一个较为合适的位置,如果实现了该方法,预设的逻辑将失效。

详见Demo中的 CYLPlusButtonSubclass 类的实现。

+ (UIViewController *)plusChildViewController;

详见: 点击 PlusButton 跳转到指定 UIViewController

另外,如果加号按钮超出了边界,一般需要手动调用如下代码取消 tabbar 顶部默认的阴影,可在 AppDelegate 类中调用:

   //去除 TabBar 自带的顶部阴影
   [[UITabBar appearance] setShadowImage:[[UIImage alloc] init]];

如何调整、自定义 PlusButton 与其它 TabBarItem 的宽度?

CYLTabBarController 规定:

TabBarItem 宽度 =  ( TabBar 总宽度 -  PlusButton 宽度  ) / (TabBarItem 个数)

所以想自定义宽度,只需要修改 PlusButton 的宽度即可。

比如你就可以在 Demo中的 CYLPlusButtonSubclass.m 类里:

[button sizeToFit]; 

改为

button.frame = CGRectMake(0.0, 0.0, 250, 100);
button.backgroundColor = [UIColor redColor];

效果如下,

enter image description here

同时你也可以顺便测试下 CYLTabBarController 的这一个特性:

即使加号按钮超出了tabbar的区域,超出部分依然能响应点击事件

并且你可以在项目中的任意位置读取到 PlusButton 的宽度,借助 CYLTabBarController.h 定义的 CYLPlusButtonWidth 这个extern。可参考 +[CYLTabBarControllerConfig customizeTabBarAppearance:] 里的用法。

补充说明

自定义 TabBar 样式

如果想更进一步的自定义 TabBar 样式可在 -application:didFinishLaunchingWithOptions: 方法中设置

/**
*  tabBarItem 的选中和不选中文字属性、背景图片
*/
- (void)customizeInterface {
   
   // 普通状态下的文字属性
   NSMutableDictionary *normalAttrs = [NSMutableDictionary dictionary];
   normalAttrs[NSForegroundColorAttributeName] = [UIColor grayColor];
   
   // 选中状态下的文字属性
   NSMutableDictionary *selectedAttrs = [NSMutableDictionary dictionary];
   selectedAttrs[NSForegroundColorAttributeName] = [UIColor darkGrayColor];
   
   // 设置文字属性
   UITabBarItem *tabBar = [UITabBarItem appearance];
   [tabBar setTitleTextAttributes:normalAttrs forState:UIControlStateNormal];
   [tabBar setTitleTextAttributes:selectedAttrs forState:UIControlStateSelected];
   
   // 设置背景图片
   UITabBar *tabBarAppearance = [UITabBar appearance];
   [tabBarAppearance setBackgroundImage:[UIImage imageNamed:@"tabbar_background"]];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/* *省略部分:   * */
   [self.window makeKeyAndVisible];
   [self customizeInterface];
   return YES;
}

横竖屏适配

TabBar 横竖屏适配时,如果你添加了 PlusButton,且适配时用到了 TabBarItem 的宽度, 不建议使用系统的UIDeviceOrientationDidChangeNotification , 请使用库里的 CYLTabBarItemWidthDidChangeNotification 来更新 TabBar 布局,最典型的场景就是,根据 TabBarItem 在不同横竖屏状态下的宽度变化来切换选中的TabBarItem 的背景图片。Demo 里 CYLTabBarControllerConfig.m 给出了这一场景的用法:

CYLTabBarController.h 中提供了 CYLTabBarItemWidth 这一extern常量,并且会在 TabBarItem 的宽度发生变化时,及时更新该值,所以用法就如下所示:

- (void)updateTabBarCustomizationWhenTabBarItemWidthDidUpdate {
   void (^deviceOrientationDidChangeBlock)(NSNotification *) = ^(NSNotification *notification) {
       [self tabBarItemWidthDidUpdate];
   };
   [[NSNotificationCenter defaultCenter] addObserverForName:CYLTabBarItemWidthDidChangeNotification
                                                     object:nil
                                                      queue:[NSOperationQueue mainQueue]
                                                 usingBlock:deviceOrientationDidChangeBlock];
}

- (void)tabBarItemWidthDidUpdate {
   UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
   if ((orientation == UIDeviceOrientationLandscapeLeft) || (orientation == UIDeviceOrientationLandscapeRight)) {
       NSLog(@"Landscape Left or Right !");
   } else if (orientation == UIDeviceOrientationPortrait){
       NSLog(@"Landscape portrait!");
   }
   CGSize selectionIndicatorImageSize = CGSizeMake(CYLTabBarItemWidth, [self cyl_tabBarController].tabBar.bounds.size.height);
   [[self cyl_tabBarController].tabBar setSelectionIndicatorImage:[[self class]
                                                                   imageFromColor:[UIColor yellowColor]
                                                                   forSize:selectionIndicatorImageSize
                                                                   withCornerRadius:0]];
}
enter image description here

访问初始化好的 CYLTabBarController 对象

对于任意 NSObject 对象:

CYLTabBarController.h 中为 NSObject 提供了分类方法 -cyl_tabBarController ,所以在任意对象中,一行代码就可以访问到一个初始化好的 CYLTabBarController 对象,-cyl_tabBarController 的作用你可以这样理解:与获取单例对象的 +shareInstance 方法作用一样。

接口如下:

// CYLTabBarController.h

@interface NSObject (CYLTabBarController)

/**
* If `self` is kind of `UIViewController`, this method will return the nearest ancestor in the view controller hierarchy that is a tab bar controller. If `self` is not kind of `UIViewController`, it will return the `rootViewController` of the `rootWindow` as long as you have set the `CYLTabBarController` as the  `rootViewController`. Otherwise return nil. (read-only)
*/
@property (nonatomic, readonly) CYLTabBarController *cyl_tabBarController;

@end

用法:

//导入 CYLTabBarController.h
#import "CYLTabBarController.h"

- (void)viewDidLoad {
   [super viewDidLoad];
   CYLTabBarController *tabbarController = [self cyl_tabBarController];
   /*...*/
}

点击 PlusButton 跳转到指定 UIViewController

提供了一个协议方法来完成本功能:

enter image description here

实现该方法后,能让 PlusButton 的点击效果与跟点击其他 UITabBarButton 效果一样,跳转到该方法指定的 UIViewController 。

注意:必须同时实现 +indexOfPlusButtonInTabBar 来指定 PlusButton 的位置。

遵循两个协议:

enter image description here

让TabBarItem仅显示图标,并使图标垂直居中

要想实现该效果,只需要在设置 tabBarItemsAttributes该属性时不传 title 即可。

比如:在Demo的基础上,注释掉图中红框部分:


enter image description here
注释前 注释后
enter image description here enter image description here

可以通过这种方式来达到 Airbnb-app 的效果:

enter image description here

如果想手动设置偏移量来达到该效果:
可以在 -setViewControllers: 方法前设置 CYLTabBarControllerimageInsetstitlePositionAdjustment 属性

这里注意:设置这两个属性后,TabBar 中所有的 TabBarItem 都将被设置。并且第一种做法的逻辑将不会执行,也就是说该做法优先级要高于第一种做法。

做法如下:


enter image description here

但是想达到Airbnb-app的效果只有这个接口是不行的,还需要自定义下 TabBar 的高度,你需要设置 CYLTabBarControllertabBarHeight 属性。你可以在Demo的 CYLTabBarControllerConfig.m 中的 -customizeTabBarAppearance: 方法中设置。

注:“仅显示图标,并使图标垂直居中”这里所指的“图标”,其所属的类是私有类: UITabBarSwappableImageView,所以 CYLTabBarController 在相关的接口命名时会包含 SwappableImageView 字样。

在 Swift 项目中使用 CYLTabBarController

参考: 《从头开始swift2.1 仿搜材通项目(三) 主流框架Tabbed的搭建》

这里注意,文章的示例代码有问题,少了设置 PlusButton 大小的代码:
这将导致 PlusButton 点击事件失效,具体修改代码如下:


enter image description here

源码实现原理

参考: 《[Note] CYLTabBarController》

更多文档信息可查看 CocoaDocs:CYLTabBarController

Q-A

Q:为什么放置6个TabBarItem会显示异常?

A:

Apple 规定:

一个 TabBar 上只能出现最多5个 TabBarItem ,第六个及更多的将不被显示。

另外注意,Apple检测的是 UITabBarItem 及其子类,所以放置“加号按钮”,这是 UIButton 不在“5个”里面。

最多只能添加5个 TabBarItem ,也就是说加上“加号按钮”,一共最多在一个 TabBar 上放置6个控件。否则第6个及之后出现 TabBarItem 会被自动屏蔽掉。而且就Apple的审核机制来说,超过5个也会被直接拒绝上架。

Q: 如何实现添加选中背景色的功能 ,像下面这样:


enter image description here

A: 这个是 iOS 系统的BUG,经测试iOS9.3已经修复了,如果在更早起版本中出现了,可以通过下面将 rootWindow 的背景色改为白色来避免:比如你可以 Appdelegate 类里这样设置:

//#import "CYLTabBarController.h"
   [[self cyl_tabBarController] rootWindow].backgroundColor = [UIColor whiteColor];

Q:我现在已经做好了一个比较简单的中间凸起的 icon 但是超过了49这个高度的位置是不能效应的 我想请问你的demo哪个功能是可以使我超出的范围也可以响应的呢?

A: 这个是自动做的,但是 CYLTabBarController 只能保证的是:只要是 UIButton 的 frame 区域内就能响应。

请把 button 的背景颜色设置为显眼的颜色,比如红色,比如像下面的plus按钮,红色部分是能接收点击事件的,但是超出了红色按钮的,黄色的图片区域,依然是无法响应点击事件的。

enter image description here

这是因为,在响应链上,UIControl 能响应点击事件, UIImage 无法响应。

(更多iOS开发干货,欢迎关注 微博@iOS程序犭袁


Posted by 微博@iOS程序犭袁
原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
<p align="center"><a href="http://weibo.com/u/1692391497?s=6uyXnP" target="_blank">

</a></a>
上一篇下一篇

猜你喜欢

热点阅读