iOS 自定义TabBarController

2023-12-14 06:38

本文主要是介绍iOS 自定义TabBarController,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、自定义的思路

iOS中的TabBarController确实已经很强大了,大部分主流iOS应用都会采用。但是往往也不能满足全部的需求,因此需要自定义TabBar,自定义需要对系统的TabBar工作方式有很好的理解,自定义需要勇气。

自定义TabBar的原则:尽量利用系统自带TabBar,只改需要改的地方。


二、自定义TabBar的总体过程
1. 先把自带的 TabBar 条给取消了
2. 自己做一个 view, 上面放几个按钮 , 设定按钮的点击事件 . 并设置 selectIndex。
3.关联各个子viewController,覆盖相关事件。

三、细节很重要

1.  让自己创建的按钮关联到 viewController:
tabbar selectedIndex 属性 . 设置这个属性就行了 .
2.  取消系统的高亮 :
可以自定义一个按钮 . 重写里面的 setHighhighted 方法 , 什么也不做就行了 .( 如果调用 super 就相当于没写 )
3.  关于几个按钮只选中一个的方法 :
设置一个属性 , 记录上一个选中的按钮 .
点击当前按钮时 , 把上一个按钮设置为未选中 , 并把当前按钮设置为选中 , 最后把当前按钮赋值给上一个按钮 .

四、初步自定义
直接上代码,详见注释。
XNTabBarController.h

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #import <UIKit/UIKit.h>  
  2.   
  3. @interface XNTabBarController : UITabBarController  
  4.   
  5. @end  

XNTabBarController.m

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  
  2. //  XNTabBarController.m  
  3. //  
  4. //  
  5. //  Created by neng on 14-6-19.  
  6. //  Copyright (c) 2014年 neng. All rights reserved.  
  7. //  
  8.   
  9. #import "XNTabBarController.h"  
  10. #import "Common.h"  
  11. #import "XNTabBarButton.h"  
  12.   
  13. @interface XNTabBarController ()  
  14. /** 
  15.  *  设置之前选中的按钮 
  16.  */  
  17. @property (nonatomic, weak) UIButton *selectedBtn;  
  18. @end  
  19.   
  20. @implementation XNTabBarController  
  21.   
  22. - (void)viewDidLoad {  
  23.     [super viewDidLoad];  
  24.     //下面两个方法在开发中是经常会用到的  
  25. //    NSLog(@"%s",__func__);  
  26. //    NSLog(@"%@",self.view.subviews); //能打印出所有子视图,和其frame  
  27.     LogFun;  
  28.     LogSubviews(self.view);  
  29.   
  30.   
  31.     //删除现有的tabBar  
  32.     CGRect rect = self.tabBar.frame;  
  33.     [self.tabBar removeFromSuperview];  //移除TabBarController自带的下部的条  
  34.   
  35.     //测试添加自己的视图  
  36.     UIView *myView = [[UIView alloc] init];  
  37.     myView.frame = rect;  
  38.     myView.backgroundColor = [UIColor redColor];  
  39.     [self.view addSubview:myView];  
  40.   
  41.     for (int i = 0; i < 5; i++) {  
  42.         //UIButton *btn = [[UIButton alloc] init];  
  43.         XNTabBarButton *btn = [[XNTabBarButton alloc] init];  
  44.           
  45.         NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];  
  46.         NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];  
  47.   
  48.         [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];  
  49.         [btn setImage:[UIImage imageNamed:imageNameSel] forState:UIControlStateSelected];  
  50.   
  51.         CGFloat x = i * myView.frame.size.width / 5;  
  52.         btn.frame = CGRectMake(x, 0, myView.frame.size.width / 5, myView.frame.size.height);  
  53.   
  54.         [myView addSubview:btn];  
  55.           
  56.         btn.tag = i;//设置按钮的标记, 方便来索引当前的按钮,并跳转到相应的视图  
  57.   
  58.         //带参数的监听方法记得加"冒号"  
  59.         [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];  
  60.   
  61.         //设置刚进入时,第一个按钮为选中状态  
  62.         if (0 == i) {  
  63.             btn.selected = YES;  
  64.             self.selectedBtn = btn;  //设置该按钮为选中的按钮  
  65.         }  
  66.     }  
  67. }  
  68.   
  69. /** 
  70.  *  自定义TabBar的按钮点击事件 
  71.  */  
  72. - (void)clickBtn:(UIButton *)button {  
  73.     //1.先将之前选中的按钮设置为未选中  
  74.     self.selectedBtn.selected = NO;  
  75.     //2.再将当前按钮设置为选中  
  76.     button.selected = YES;  
  77.     //3.最后把当前按钮赋值为之前选中的按钮  
  78.     self.selectedBtn = button;  
  79.       
  80.     //4.跳转到相应的视图控制器. (通过selectIndex参数来设置选中了那个控制器)  
  81.     self.selectedIndex = button.tag;  
  82. }  
  83.   
  84. @end  

XNTabBarButton.h

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #import <UIKit/UIKit.h>  
  2.   
  3. @interface XNTabBarButton : UIButton  
  4.   
  5. @end  

XNTabBarButton.m

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #import "XNTabBarButton.h"  
  2.   
  3. @implementation XNTabBarButton  
  4. /**什么也不做就可以取消系统按钮的高亮状态*/  
  5. - (void)setHighlighted:(BOOL)highlighted{  
  6. //    [super setHighlighted:highlighted];  
  7. }  
  8.   
  9. @end  

五、代码重构

重构 的目的是把代码放到他最该到的地方去 . 提高可读写与可拓展性。

对控件的重构要 保证可重用性 .  做到封装做其他应用时 , 可以直接拿过去用的地步 .

tips :
1、 关于 init initWithFrame :
在对象初始化调用 init , 会调用 initWithFrame 方法 .
Init initWithFrame 都会被调用 .
建议自定义控件不要重 init 方法 , 需要 初始化 重写 initWithFrame 方法 .
好处 : 其他人调用无论是调用 init, 还是调用 initWithFrame 都会调用 initWithFrame 方法 .

2、 关于控件的 布局代码 :
建议写在 layoutSubviews 方法中 .
不要忘记写 super 方法
将设置 x,y,frame 等写在这里面 .
3、自定义的Tabbar 添加为系统 TabBar 的子视图,这样TabBar的切换自动隐藏/滑动功能就不用自己做了. (hidebottombaronpush)

重构后的代码如下
将自定义的TabBar单独建立,并将代码移过去。
设置代理方法,工具栏按钮被选中,记录从哪里跳转到哪里. 

XNTabBar.h

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #import <UIKit/UIKit.h>  
  2. @class XNTabBar;  
  3.   
  4. @protocol XNTabBarDelegate <NSObject>  
  5. /** 
  6.  *  工具栏按钮被选中, 记录从哪里跳转到哪里. (方便以后做相应特效) 
  7.  */  
  8. - (void) tabBar:(XNTabBar *)tabBar selectedFrom:(NSInteger) from to:(NSInteger)to;  
  9.   
  10. @end  
  11.   
  12. @interface XNTabBar : UIView  
  13. @property(nonatomic,weak) id<XNTabBarDelegate> delegate;  
  14. /** 
  15.  *  使用特定图片来创建按钮, 这样做的好处就是可扩展性. 拿到别的项目里面去也能换图片直接用 
  16.  * 
  17.  *  @param image         普通状态下的图片 
  18.  *  @param selectedImage 选中状态下的图片 
  19.  */  
  20. -(void)addButtonWithImage:(UIImage *)image selectedImage:(UIImage *) selectedImage;  
  21. @end  


XNTabBar.m


[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  
  2. //  XNTabBar.m  
  3. //  
  4. //  Created by neng on 14-6-19.  
  5. //  Copyright (c) 2014年 neng. All rights reserved.  
  6. //  
  7.   
  8. #import "XNTabBar.h"  
  9. #import "XNTabBarButton.h"  
  10.   
  11. @interface XNTabBar ()  
  12. /** 
  13.  *  设置之前选中的按钮 
  14.  */  
  15. @property (nonatomic, weak) UIButton *selectedBtn;  
  16. @end  
  17.   
  18. @implementation XNTabBar  
  19.   
  20. /** 
  21.  *  在这个方法里写控件初始化的东西, 调用init方法时会调用 
  22.  */  
  23. //- (id)initWithFrame:(CGRect)frame {  
  24. //  if (self = [super initWithFrame:frame]) {  
  25. //      //添加按钮  
  26. //      for (int i = 0; i < 5; i++) { //取消掉特定的数字  
  27. //          //UIButton *btn = [[UIButton alloc] init];  
  28. //          XNTabBarButton *btn = [[XNTabBarButton alloc] init];  
  29. //  
  30. //          NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];  
  31. //          NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];  
  32. //  
  33. //          [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];  
  34. //          [btn setImage:[UIImage imageNamed:imageNameSel] forState:UIControlStateSelected];  
  35. //  
  36. //          [self addSubview:btn];  
  37. //  
  38. //          btn.tag = i; //设置按钮的标记, 方便来索引当前的按钮,并跳转到相应的视图  
  39. //  
  40. //          //带参数的监听方法记得加"冒号"  
  41. //          [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];  
  42. //  
  43. //          if (0 == i) {  
  44. //              [self clickBtn:btn];  
  45. //          }  
  46. //      }  
  47. //  }  
  48. //  return self;  
  49. //}  
  50.   
  51. - (void)addButtonWithImage:(UIImage *)image selectedImage:(UIImage *)selectedImage {  
  52.     UIButton *btn = [[UIButton alloc] init];  
  53.   
  54.     [btn setImage:image forState:UIControlStateNormal];  
  55.     [btn setImage:selectedImage forState:UIControlStateSelected];  
  56.   
  57.     [self addSubview:btn];  
  58.   
  59.     //带参数的监听方法记得加"冒号"  
  60.     [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];  
  61.   
  62.     //如果是第一个按钮, 则选中(按顺序一个个添加)  
  63.     if (self.subviews.count == 1) {  
  64.         [self clickBtn:btn];  
  65.     }  
  66. }  
  67.   
  68. /**专门用来布局子视图, 别忘了调用super方法*/  
  69. - (void)layoutSubviews {  
  70.     [super layoutSubviews];  
  71.   
  72.     int count = self.subviews.count;  
  73.     for (int i = 0; i < count; i++) {  
  74.         //取得按钮  
  75.         UIButton *btn = self.subviews[i];  
  76.   
  77.         btn.tag = i; //设置按钮的标记, 方便来索引当前的按钮,并跳转到相应的视图  
  78.   
  79.         CGFloat x = i * self.bounds.size.width / count;  
  80.         CGFloat y = 0;  
  81.         CGFloat width = self.bounds.size.width / count;  
  82.         CGFloat height = self.bounds.size.height;  
  83.         btn.frame = CGRectMake(x, y, width, height);  
  84.     }  
  85. }  
  86.   
  87. /** 
  88.  *  自定义TabBar的按钮点击事件 
  89.  */  
  90. - (void)clickBtn:(UIButton *)button {  
  91.     //1.先将之前选中的按钮设置为未选中  
  92.     self.selectedBtn.selected = NO;  
  93.     //2.再将当前按钮设置为选中  
  94.     button.selected = YES;  
  95.     //3.最后把当前按钮赋值为之前选中的按钮  
  96.     self.selectedBtn = button;  
  97.   
  98.     //却换视图控制器的事情,应该交给controller来做  
  99.     //最好这样写, 先判断该代理方法是否实现  
  100.     if ([self.delegate respondsToSelector:@selector(tabBar:selectedFrom:to:)]) {  
  101.         [self.delegate tabBar:self selectedFrom:self.selectedBtn.tag to:button.tag];  
  102.     }  
  103.   
  104.     //4.跳转到相应的视图控制器. (通过selectIndex参数来设置选中了那个控制器)  
  105.     //self.selectedIndex = button.tag;  
  106. }  
  107.   
  108. @end  

原先的 XNTabBarController.m 经过修改后,注释了原先的代码。

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  
  2. //  XNTabBarController.m  
  3. //  
  4. //  Created by neng on 14-6-19.  
  5. //  Copyright (c) 2014年 neng. All rights reserved.  
  6. //  
  7.   
  8. #import "XNTabBarController.h"  
  9. #import "XNTabBarButton.h"  
  10. #import "XNTabBar.h"  
  11.   
  12. @interface XNTabBarController () <XNTabBarDelegate>  
  13. /** 
  14.  *  设置之前选中的按钮 
  15.  */  
  16. @property (nonatomic, weak) UIButton *selectedBtn;  
  17. @end  
  18.   
  19. @implementation XNTabBarController  
  20.   
  21. - (void)viewDidLoad {  
  22.     [super viewDidLoad];  
  23.     //下面两个方法在开发中是经常会用到的  
  24. //    NSLog(@"%s",__func__);  
  25. //    NSLog(@"%@",self.view.subviews); //能打印出所有子视图,和其frame  
  26. //  LogFun;  
  27. //  LogSubviews(self.view);  
  28.     //Hell  
  29.   
  30.   
  31.     //删除现有的tabBar  
  32.     CGRect rect = self.tabBar.bounds//这里要用bounds来加, 否则会加到下面去.看不见  
  33.     LogFrame(self.tabBar);  
  34.     //[self.tabBar removeFromSuperview];  //移除TabBarController自带的下部的条  
  35.   
  36.     //测试添加自己的视图  
  37.     XNTabBar *myView = [[XNTabBar alloc] init]; //设置代理必须改掉前面的类型,不能用UIView  
  38.     myView.delegate = self//设置代理  
  39.     myView.frame = rect;  
  40.     [self.tabBar addSubview:myView]; //添加到系统自带的tabBar上, 这样可以用的的事件方法. 而不必自己去写  
  41.       
  42.     //为控制器添加按钮  
  43.     for (int i=0; i<self.viewControllers.count; i++) { //根据有多少个子视图控制器来进行添加按钮  
  44.           
  45.         NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];  
  46.         NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];  
  47.           
  48.         UIImage *image = [UIImage imageNamed:imageName];  
  49.         UIImage *imageSel = [UIImage imageNamed:imageNameSel];  
  50.           
  51.         [myView addButtonWithImage:image selectedImage:imageSel];  
  52.     }  
  53.       
  54.   
  55. //    //添加按钮  
  56. //  for (int i = 0; i < 5; i++) {  
  57. //      //UIButton *btn = [[UIButton alloc] init];  
  58. //        XNTabBarButton *btn = [[XNTabBarButton alloc] init];  
  59. //  
  60. //      NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];  
  61. //      NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];  
  62. //  
  63. //      [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];  
  64. //      [btn setImage:[UIImage imageNamed:imageNameSel] forState:UIControlStateSelected];  
  65. //  
  66. //      CGFloat x = i * myView.frame.size.width / 5;  
  67. //      btn.frame = CGRectMake(x, 0, myView.frame.size.width / 5, myView.frame.size.height);  
  68. //  
  69. //      [myView addSubview:btn];  
  70. //  
  71. //        btn.tag = i;//设置按钮的标记, 方便来索引当前的按钮,并跳转到相应的视图  
  72. //  
  73. //      //带参数的监听方法记得加"冒号"  
  74. //      [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];  
  75. //  
  76. //      //设置刚进入时,第一个按钮为选中状态  
  77. //      if (0 == i) {  
  78. //          btn.selected = YES;  
  79. //          self.selectedBtn = btn;  //设置该按钮为选中的按钮  
  80. //      }  
  81. //  }  
  82. }  
  83.   
  84. /**永远别忘记设置代理*/  
  85. - (void)tabBar:(XNTabBar *)tabBar selectedFrom:(NSInteger)from to:(NSInteger)to {  
  86.     self.selectedIndex = to;  
  87. }  
  88.   
  89. /** 
  90.  *  自定义TabBar的按钮点击事件 
  91.  */  
  92. //- (void)clickBtn:(UIButton *)button {  
  93. //  //1.先将之前选中的按钮设置为未选中  
  94. //  self.selectedBtn.selected = NO;  
  95. //  //2.再将当前按钮设置为选中  
  96. //  button.selected = YES;  
  97. //  //3.最后把当前按钮赋值为之前选中的按钮  
  98. //  self.selectedBtn = button;  
  99. //  
  100. //    //4.跳转到相应的视图控制器. (通过selectIndex参数来设置选中了那个控制器)  
  101. //    self.selectedIndex = button.tag;  
  102. //}  
  103.   
  104. @end  

自定义后的效果图:




例子源码下载 : http://download.csdn.net/detail/xn4545945/7572263

这篇关于iOS 自定义TabBarController的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/491499

相关文章

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...

SpringBoot 自定义消息转换器使用详解

《SpringBoot自定义消息转换器使用详解》本文详细介绍了SpringBoot消息转换器的知识,并通过案例操作演示了如何进行自定义消息转换器的定制开发和使用,感兴趣的朋友一起看看吧... 目录一、前言二、SpringBoot 内容协商介绍2.1 什么是内容协商2.2 内容协商机制深入理解2.2.1 内容

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

Oracle type (自定义类型的使用)

oracle - type   type定义: oracle中自定义数据类型 oracle中有基本的数据类型,如number,varchar2,date,numeric,float....但有时候我们需要特殊的格式, 如将name定义为(firstname,lastname)的形式,我们想把这个作为一个表的一列看待,这时候就要我们自己定义一个数据类型 格式 :create or repla

HTML5自定义属性对象Dataset

原文转自HTML5自定义属性对象Dataset简介 一、html5 自定义属性介绍 之前翻译的“你必须知道的28个HTML5特征、窍门和技术”一文中对于HTML5中自定义合法属性data-已经做过些介绍,就是在HTML5中我们可以使用data-前缀设置我们需要的自定义属性,来进行一些数据的存放,例如我们要在一个文字按钮上存放相对应的id: <a href="javascript:" d

一步一步将PlantUML类图导出为自定义格式的XMI文件

一步一步将PlantUML类图导出为自定义格式的XMI文件 说明: 首次发表日期:2024-09-08PlantUML官网: https://plantuml.com/zh/PlantUML命令行文档: https://plantuml.com/zh/command-line#6a26f548831e6a8cPlantUML XMI文档: https://plantuml.com/zh/xmi