在上一篇文章 iOS 项目基本框架搭建中, 我们详细说明了如何对 TabBarItem 的图片属性以及文字属性进行一些自定义配置但是, 很多时候, 我们需要修改 TabBarItem 的图片和文字属性之外, 还需要自定义 TabBarItem 的位置, 这样系统自带的 TabBar 的样式并不能满足我们的项目需求, 所以我们需要对系统的 UITabBar 进行自定义, 以达到我们的项目需求例如新浪微博 App 的底部 tab 的 item 就无法用自带的 TabBarItem 进行实现, 最中间那个 + 发布微博并不是用来切换 tab 的, 而是在当前的页面上覆盖一个编辑发布页面, 发布完成或者取消发布之后又回到之前的页面, 并没有进行切换, 这时候我们就需要对 TabBar 进行自定义, 在最中间空出一个 TabBar 的空间进行布置这个 + 发布按钮
我们的项目是仿写百思不得姐 App 的功能模块进行学习和提高, 其 TabBar 的样式与微博的样式基本相似 (如上图右边的图), 最中间的 Tab 按钮也是发帖功能, 也是在直接当前的页面上覆盖一个编辑发布页面, 发布完成或者取消发布之后又回到之前的页面, 并没有进行切换
. 解决方案
对于类似新浪微博和我们项目中这种情况有两种解决思路:
定义 5 个 TabBarItem, 然后在 TabBar 上添加一个与 TabBarItem 等大小的发布按钮在最中间, 并添加点击事件, 这样因为大小相等, 所以新按钮完全覆盖了最中间的 TabBarItem, 最中间的 TabBarItem 的响应事件也会被屏蔽, 因为按钮会先响应
自定义 TabBar, 重写其 layoutSubviews 方法, 将所有 4 个 TabBarItem 的布局和大小进行修改, 将中间空出来, 然后添加一个自定义的发布按钮, 实现其点击事件即可
1 覆盖控件实现方案
这种方案的思路在上面已经说到了, 就是先占一个位置, 然后用一个按钮覆盖到其上面主要缺点就是需要先申请一个位置和控制器来占位比较浪费, 而且这种也只适用于各控件的大小是均匀的情况, 当我们需求中每个 TabBarItem 的规格和尺寸不一样时, 我们就无法使用这种方案实现
有几点值得说明一下:
设置所有 UITabBarItem 的文字属性在上一篇文章 iOS 项目基本框架搭建中已经提到了, 这里就不详细介绍了
发布按钮的初始化应该使用单例模式进行创建, 因为我们项目中只有一个发布按钮, 所以使用单例模式更合理, 本文采用懒加载的方式进行单例模式的创建
在 viewWillAppear: 中添加发布按钮 [self.tabBar addSubview:self.publishButton]; 至于为什么要在 viewWillAppear: 中添加发布按钮而不是在 viewDidLoad 中添加? 根本原因就是 TabBarItem 加载到 TabBar 上是在 viewDidLoad 之后执行的, 所以, 如果添加发布按钮在 viewDidLoad 中会造成发布按钮在 TabBar 中是第一个添加的, 这样会导致发布按钮会被 TabBarItem 覆盖了, 这样我们就达到我们的目的
- #import "XMGTabBarController.h"
- @interface XMGTabBarController ()
- /** 中间的发布按钮 */
- @property (nonatomic, strong) UIButton *publishButton;
- @end
- @implementation XMGTabBarController
- #pragma mark - 初始化
- - (void)viewDidLoad {
- [super viewDidLoad];
- /**** 设置所有 UITabBarItem 的文字属性 ****/
- UITabBarItem *item = [UITabBarItem appearance];
- // 普通状态下的文字属性
- NSMutableDictionary *normalAttrs = [NSMutableDictionary dictionary];
- normalAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:14];
- normalAttrs[NSForegroundColorAttributeName] = [UIColor grayColor];
- [item setTitleTextAttributes:normalAttrs forState:UIControlStateNormal];
- // 选中状态下的文字属性
- NSMutableDictionary *selectedAttrs = [NSMutableDictionary dictionary];
- selectedAttrs[NSForegroundColorAttributeName] = [UIColor darkGrayColor];
- [item setTitleTextAttributes:normalAttrs forState:UIControlStateSelected];
- /**** 添加子控制器 ****/
- [self setupOneChildViewController:[[UITableViewController alloc] init] title:@"精华" image:@"tabBar_essence_icon" selectedImage:@"tabBar_essence_click_icon"];
- [self setupOneChildViewController:[[UITableViewController alloc] init] title:@"新帖" image:@"tabBar_new_icon" selectedImage:@"tabBar_new_click_icon"];
- // 中间用来占位的子控制器
- [self setupOneChildViewController:[[UIViewController alloc] init] title:nil image:nil selectedImage:nil];
- [self setupOneChildViewController:[[UIViewController alloc] init] title:@"关注" image:@"tabBar_friendTrends_icon" selectedImage:@"tabBar_friendTrends_click_icon"];
- [self setupOneChildViewController:[[UITableViewController alloc] init] title:@"我" image:@"tabBar_me_icon" selectedImage:@"tabBar_me_click_icon"];
- }
- /**
- * 为什么要在 viewWillAppear: 方法中添加发布按钮?
- * 当 viewWillAppear: 方法被调用的时候, tabBar 内部已经添加了 5 个 UITabBarButton
- * 就可以实现一个效果 : [发布按钮] 盖在其他 UITabBarButton 上面
- */
- - (void)viewWillAppear:(BOOL)animated
- {
- [super viewWillAppear:animated];
- /**** 增加一个发布按钮 ****/
- [self.tabBar addSubview:self.publishButton];
- }
- #pragma mark - 懒加载
- /** 发布按钮 */
- - (UIButton *)publishButton
- {
- if (!_publishButton) {
- _publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
- _publishButton.backgroundColor = XMGRandomColor;
- [_publishButton setImage:[UIImage imageNamed:@"tabBar_publish_icon"] forState:UIControlStateNormal];
- [_publishButton setImage:[UIImage imageNamed:@"tabBar_publish_click_icon"] forState:UIControlStateHighlighted];
- _publishButton.frame = CGRectMake(0, 0, self.tabBar.frame.size.width / 5, self.tabBar.frame.size.height);
- _publishButton.center = CGPointMake(self.tabBar.frame.size.width * 0.5, self.tabBar.frame.size.height * 0.5);
- [_publishButton addTarget:self action:@selector(publishClick) forControlEvents:UIControlEventTouchUpInside];
- }
- return _publishButton;
- }
- #pragma mark - 监听
- /**
- * 发布按钮点击
- */
- - (void)publishClick
- {
- XMGLogFunc
- XMGTestViewController *test = [[XMGTestViewController alloc] init];
- [self presentViewController:test animated:YES completion:nil];
- }
- @end
2 自定义 TabBar
自定义 TabBar 可以完全按照我们的需求来布局和配置 TabBar 中各子控件的属性和布局
- @interface XMGTabBarController ()
- @end
- @implementation XMGTabBarController
- #pragma mark - 初始化
- - (void)viewDidLoad {
- [super viewDidLoad];
- /**** 设置所有 UITabBarItem 的文字属性 ****/
- // 省略
- /**** 添加子控制器 ****/
- [self setupOneChildViewController:[[UITableViewController alloc] init] title:@"精华" image:@"tabBar_essence_icon" selectedImage:@"tabBar_essence_click_icon"];
- [self setupOneChildViewController:[[UITableViewController alloc] init] title:@"新帖" image:@"tabBar_new_icon" selectedImage:@"tabBar_new_click_icon"];
- [self setupOneChildViewController:[[UIViewController alloc] init] title:@"关注" image:@"tabBar_friendTrends_icon" selectedImage:@"tabBar_friendTrends_click_icon"];
- [self setupOneChildViewController:[[UITableViewController alloc] init] title:@"我" image:@"tabBar_me_icon" selectedImage:@"tabBar_me_click_icon"];
- /**** 更换 TabBar ****/
- [self setValue:[[XMGTabBar alloc] init] forKeyPath:@"tabBar"];
- }
- @end
下面的代码是我们自定义 TabBar 的. m 文件的主要内容, 主要是重写其 layoutSubviews 方法, 在该方法中我们是将四个按钮的大小和布局进行了调整, 然后在最中间添加一个发布按钮同样的, 也有几点需要注意的:
发布按钮的初始化还是和上面一样, 应该采用单例模式进行初始化, 具体就不展开;
重写 layoutSubviews 方法时, 应该先调用其父类的此方法 [super layoutSubviews]; , 这样可以先对 TabBarItem 进行布局, 然后在此布局的基础上进行布局调整调用父类布局方法的语句不能放在后面, 更不能省略, 因为此方法除了对 TabBarItem 进行布局之外还有很多其他的配置;
通过 self.subviews 来获取当前的子控件, 我们可以先进行打印了解当前子控件的类型和数量, 然后进行适当的筛选出我们需要修改的控件进行布局, 我们一般这里采用 KVC 的方法进行获取和修改, 例如上面我们修改当前的 TabBar [self setValue:[[XMGTabBar alloc] init] forKeyPath:@"tabBar"]; , 关于如何获取属性和成员变量可以参见: 三分钟教会你 runtime 获取属性和成员变量
- #import "XMGTabBar.h"
- @interface XMGTabBar()
- /** 中间的发布按钮 */
- @property (nonatomic, weak) UIButton *publishButton;
- @end
- @implementation XMGTabBar
- #pragma mark - 懒加载
- /** 发布按钮 */
- - (UIButton *)publishButton
- {
- if (!_publishButton) {
- UIButton *publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
- publishButton.backgroundColor = XMGRandomColor;
- [publishButton setImage:[UIImage imageNamed:@"tabBar_publish_icon"] forState:UIControlStateNormal];
- [publishButton setImage:[UIImage imageNamed:@"tabBar_publish_click_icon"] forState:UIControlStateHighlighted];
- [publishButton addTarget:self action:@selector(publishClick) forControlEvents:UIControlEventTouchUpInside];
- [self addSubview:publishButton];
- _publishButton = publishButton;
- }
- return _publishButton;
- }
- #pragma mark - 初始化
- /**
- * 布局子控件
- */
- - (void)layoutSubviews
- {
- [super layoutSubviews];
- /**** 设置所有 UITabBarButton 的 frame ****/
- // 按钮的尺寸
- CGFloat buttonW = self.frame.size.width / 5;
- CGFloat buttonH = self.frame.size.height;
- CGFloat buttonY = 0;
- // 按钮索引
- int buttonIndex = 0;
- for (UIView *subview in self.subviews) {
- // 过滤掉非 UITabBarButton
- // if (![@"UITabBarButton" isEqualToString:NSStringFromClass(subview.class)]) continue;
- if (subview.class != NSClassFromString(@"UITabBarButton")) continue;
- // 设置 frame
- CGFloat buttonX = buttonIndex * buttonW;
- if (buttonIndex >= 2) { // 右边的 2 个 UITabBarButton
- buttonX += buttonW;
- }
- subview.frame = CGRectMake(buttonX, buttonY, buttonW, buttonH);
- // 增加索引
- buttonIndex++;
- }
- /**** 设置中间的发布按钮的 frame ****/
- self.publishButton.frame = CGRectMake(0, 0, buttonW, buttonH);
- self.publishButton.center = CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5);
- }
- #pragma mark - 监听
- - (void)publishClick
- {
- XMGLogFunc
- }
- @end
3 添加红点提示
现在很多 App 的 TabBarItem 在有新消息时在右上角会有一个红点提示, 有的甚至还会有具体数目的提醒, 类似我们常用的 QQ 微信微博头条等都会有类似的功能, 这个提示在 iOS 中的学名叫做 badge(其实是一般都这么命名而已) 在 iOS 的 TabBarItem 是自带该属性和控件的, 我们可以根据自己的需求进行配置, 下图是 iOS11 中的配置文档, 可以对提示数量颜色进行自定义设置, 还可以对提示文字的属性进行不同状态下的配置
据说在 iOS10 之前对 badge 的提示颜色是不能进行配置的, 这时候如果需要, 我们就只能进行自定义 TabBarItem, 然后对自定义的 badge 进行配置本文就不对这一点进行详细说明了, 有需要的小伙伴可以自行求助度娘
来源: https://www.cnblogs.com/mukekeheart/p/8420704.html