实现效果如下:
*首先介绍一下组成部分:
1.有一个UITabBarController用来控制基本的四个页面(四个页面又作为NavigationController的根视图: 方便跳转)
2.有一个UITabBar视图, 就是我们要自定义的tabBar视图. 我们在上面进行布局
*视图的层次结构:(上为根的根视图)
(上)UIViewController--> UINavigationController--> UITabBarController(根)
*工程结构
第一步: 首先在AppDelegate文件中, 将TabBarController 设置为window的根视图控制器
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
//创建Tabbar
LBTabBarController *tabBarVc = [[LBTabBarController alloc] init];
//添加转场动画
CATransition *anim = [[CATransition alloc] init];
anim.type = @"rippleEffect";
anim.duration = 1.0;
[self.window.layer addAnimation:anim forKey:nil];
# 将TabBar指定根视图控制器
self.window.rootViewController = tabBarVc;
[self.window makeKeyAndVisible];
return YES;
}
第二步: 创建TabBarController控制器,在TabBarController.m中设置被管理的子视图控制器
#import "LBTabBarController.h"
#import "LBNavigationController.h"
//四个基本的视图控制器页面
#import "LBMessageVC.h"
#import "LBFishVC.h"
#import "LBHomeVC.h"
#import "LBMineVC.h"
//自定义的tabBar
#import "LBTabBar.h"
@interface LBTabBarController ()<LBTabBarDelegate>
@end
@implementation LBTabBarController
#pragma mark - 第一次使用当前类的时候对设置UITabBarItem的主题
/*
+ (void)initialize
{
UITabBarItem *tabBarItem = [UITabBarItem appearanceWhenContainedInInstancesOfClasses:@[self]];
NSMutableDictionary *dictNormal = [NSMutableDictionary dictionary];
dictNormal[NSForegroundColorAttributeName] = [UIColor grayColor];
dictNormal[NSFontAttributeName] = [UIFont systemFontOfSize:11];
NSMutableDictionary *dictSelected = [NSMutableDictionary dictionary];
dictSelected[NSForegroundColorAttributeName] = [UIColor darkGrayColor];
dictSelected[NSFontAttributeName] = [UIFont systemFontOfSize:11];
[tabBarItem setTitleTextAttributes:dictNormal forState:UIControlStateNormal];
[tabBarItem setTitleTextAttributes:dictSelected forState:UIControlStateSelected];
}
*/
- (void)viewDidLoad {
[super viewDidLoad];
#第一步:
[self setUpAllChildVC]; //调用该类的私有方法, 初始化设置tabBar上的所有子视图
#第二步:
LBTabBar *tabBar = [[LBTabBar alloc] init]; //初始化自定义的tabBar视图
tabBar.MyDelegate = self; //设置代理
[self setValue:tabBar forKey:@"tabBar"]; //替换原有的tabBar视图
}
#第一步方法: 初始化tabBar上除了中间按钮之外所有的按钮
- (void)setUpAllChildVC{
LBHomeVC *HomeVC = [LBHomeVC new];
[self setUpOneChildVcWithVc:HomeVC Image:@"home_normal" selectedImage:@"home_highlight" title:@"首页"];
LBFishVC *FishVC = [LBFishVC new];
[self setUpOneChildVcWithVc:FishVC Image:@"fish_normal" selectedImage:@"fish_highlight" title:@"鱼塘"];
LBMessageVC *MessageVC = [[LBMessageVC alloc] init];
[self setUpOneChildVcWithVc:MessageVC Image:@"message_normal" selectedImage:@"message_highlight" title:@"消息"];
LBMineVC *MineVC = [[LBMineVC alloc] init];
[self setUpOneChildVcWithVc:MineVC Image:@"account_normal" selectedImage:@"account_highlight" title:@"我的"];
}
#pragma mark ------------ 具体的赋值方法 ----------------
/*为每一个视图控制器指定对应的tabBarButton样式
1. 设置每个按钮对应的VC
2. 设置每个按钮对应的普通状态下的图片
3. 设置每个按钮对应的选中状态下的图片
4. 设置每个按钮对应的标题
*/
- (void)setUpOneChildVcWithVc:(UIViewController *)Vc Image:(NSString *)image selectedImage:(NSString *)selectedImage title:(NSString *)title{
# 当然实现该方法的时候, 需要去创建NavigationController了
LBNavigationController *nav = [[LBNavigationController alloc] initWithRootViewController:Vc];
Vc.view.backgroundColor = [self randomColor];
UIImage *MyImage = [UIImage imageNamed:image];
MyImage = [MyImage imageWithRenderingMode:(UIImageRenderingModeAlwaysOriginal)];
Vc.tabBarItem.image = MyImage;
UIImage *selImage = [UIImage imageNamed:selectedImage];
selImage = [selImage imageWithRenderingMode:(UIImageRenderingModeAlwaysOriginal)];
Vc.tabBarItem.selectedImage = selImage;
Vc.tabBarItem.title = title;
Vc.navigationItem.title = title;
[self addChildViewController:nav];
}
#pragma mark - LBTabBarDelegate
//点击中间按钮的代理方法
- (void)tabBarPlusBtnClick:(LBTabBar *)tabBar
{
NSLog(@"AAAAAAAAAAAAAAA");
}
#pragma mark - 随机颜色的实现方法
- (UIColor *)randomColor
{
CGFloat r = arc4random_uniform(256);
CGFloat g = arc4random_uniform(256);
CGFloat b = arc4random_uniform(256);
return [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0];
}
@end
第三步: 文件三NavigationController.m文件
#import "LBNavigationController.h"
#import "UIImage+Image.h"
//黄色导航栏
#define NavBarColor [UIColor colorWithRed:250/255.0 green:227/255.0 blue:111/255.0 alpha:1.0]
@interface LBNavigationController ()
@end
@implementation LBNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
}
+ (void)load{
//设置tabbar的文字属性
UIBarButtonItem *item = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[self]];
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
dic[NSFontAttributeName] = [UIFont systemFontOfSize:15];
dic[NSForegroundColorAttributeName] = [UIColor blackColor];
[item setTitleTextAttributes:dic forState:UIControlStateNormal];
//设置navigationBar的属性
UINavigationBar *navBar = [UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[self]];
[navBar setBackgroundImage:[UIImage imageWithColor:NavBarColor] forBarMetrics:UIBarMetricsDefault];
NSMutableDictionary *navDic = [NSMutableDictionary dictionary];
navDic[NSFontAttributeName] = [UIFont systemFontOfSize:15];
[navBar setTitleTextAttributes:navDic];
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (self.viewControllers.count > 0) {
//隐藏tabBar
viewController.hidesBottomBarWhenPushed = YES;
}
return [super pushViewController:viewController animated:YES];
}
@end
第四步: 自定义TabBar的具体实现文件了
tabBar.h文件
#import <UIKit/UIKit.h>
@class LBTabBar;
//声明一个协议 + 协议方法
@protocol LBTabBarDelegate <NSObject>
@optional
- (void)tabBarPlusBtnClick:(LBTabBar *)tabBar;
@end
@interface LBTabBar : UITabBar
//设置代理属性
@property (weak, nonatomic) id<LBTabBarDelegate> MyDelegate;
@end
tabBar.m文件
#import "LBTabBar.h"
#import "UIImage+Image.h"
#import "UIView+LBExtension.h"
#define LBMagin 10
@interface LBTabBar ()
//按钮
@property (nonatomic, weak) UIButton *plusBtn;
@end
@implementation LBTabBar
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor whiteColor];
[self setShadowImage:[UIImage imageWithColor:[UIColor clearColor]]];
//设置按钮不同状态时的图片
UIButton *plusBtn = [[UIButton alloc] init];
[plusBtn setBackgroundImage:[UIImage imageNamed:@"post_normal"] forState:UIControlStateNormal];
[plusBtn setBackgroundImage:[UIImage imageNamed:@"post_normal"] forState:UIControlStateHighlighted];
//指定按钮
self.plusBtn = plusBtn;
//给按钮添加事件
[self.plusBtn addTarget: self action:@selector(plusBtnDidClick) forControlEvents:UIControlEventTouchUpInside];
//添加到视图上
[self addSubview:plusBtn];
}
return self;
}
//点击发布按钮事件实现
- (void)plusBtnDidClick{
//如果tabbar的代理实现了对应的代理方法, 那么就调用代理的方法
if ([self.delegate respondsToSelector:@selector(tabBarPlusBtnClick:)]) {
//实现代理方法
[self.MyDelegate tabBarPlusBtnClick:self];
}
}
- (void)layoutSubviews{
[super layoutSubviews];
//这里需要调用一个延展类的方法(文章下面说明内部实现)
self.plusBtn.centerX = self.centerX;
//调整发布按钮的中线点Y值
self.plusBtn.centerY = self.height * 0.5 - 2 *LBMagin;
self.plusBtn.size = CGSizeMake(self.plusBtn.currentBackgroundImage.size.width, self.plusBtn.currentBackgroundImage.size.height);
//文字
UILabel *label = [[UILabel alloc] init];
label.text = @"发布";
label.font = [UIFont systemFontOfSize:11];
[label sizeToFit];
label.textColor = [UIColor grayColor];
[self addSubview:label];
label.centerX = self.plusBtn.centerX;
label.centerY = CGRectGetMaxY(self.plusBtn.frame) + LBMagin ;
//系统自带的按钮类型是UITabBarButton, 找出这些类型的按钮, 然后重新排布位置, 空出中间的位置
Class class = NSClassFromString(@"UITabBarButton");
int btnIndex = 0;
for (UIView *btn in self.subviews) {//遍历tabBar的子控件
//判断是系统自带的视图
if ([btn isKindOfClass:class]) {
//么就调整子控件位置,空出中间位置
//每个按钮的宽度 == tabBar的五分之一
btn.width = self.width / 5;
btn.x = btn.width * btnIndex;
btnIndex++;
//如果索引是2 (从0)开始) , 则直接让索引++(目的就是让消息按钮的位置向右移动,空出来发布按钮的位置)
if (btnIndex == 2) {
btnIndex++;
}
}
}
[self bringSubviewToFront:self.plusBtn];
}
//重写hitTest方法, 去监听发布按钮的点击, 目的是为了让凸出的部分也能响应点击事件
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
//这是一个判断的关键, 不判断的话push到其他页面, 点击发布按钮的位置也是会有反应的, 这样就不好了
//self.isHidden == NO 说明当前页面是有tabbar的,那么肯定是在导航控制器的根控制器页面
//在导航控制器根控制器页面,那么我们就需要判断手指点击的位置是否在发布按钮身上
//是的话让发布按钮自己处理点击事件,不是的话让系统去处理点击事件就可以了
if (self.isHidden == NO) {
//将当前tabbar的触摸点转换坐标系,转换到发布按钮的身上,生成一个新的点
CGPoint newP = [self convertPoint:point toView:self.plusBtn];
//判断如果这个新的点是在发布按钮身上,那么处理点击事件最合适的view就是发布按钮
if ( [self.plusBtn pointInside:newP withEvent:event]) {
return self.plusBtn;
}else{//如果点不在发布按钮身上,直接让系统处理就可以了
return [super hitTest:point withEvent:event];
}
}
else {//tabbar隐藏了,那么说明已经push到其他的页面了,这个时候还是让系统去判断最合适的view处理就好了
return [super hitTest:point withEvent:event];
}
}
@end
两个延展类的实现
UIImage的延展实现:
#----------------------------UIImage+image.h文件
@interface UIImage (Image)
/*
根据颜色生成一张图片
@param imageName提供的颜色
*/
+(UIImage *)imageWithColor:(UIColor *)color;
@end
#---------------------------UIImage+image.m文件
#import "UIImage+Image.h"
@implementation UIImage (Image)
/*
根据颜色生成一张图片
@param imageName提供的颜色
*/
+(UIImage *)imageWithColor:(UIColor *)color{
//描述一个矩形
CGRect rect = CGRectMake(0, 0, 1, 1);
//开始图形上下文
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
//获得图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//使用color填充山下文
CGContextSetFillColorWithColor(ctx, [color CGColor]);
//渲染上下文
CGContextFillRect(ctx, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//关闭上下文
UIGraphicsEndImageContext();
return image;
}
@end
UIView类的延展
#UIView+LBExtension.h-------------------------
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface UIView (LBExtension)
@property (nonatomic, assign)CGFloat x;
@property (nonatomic, assign)CGFloat width;
@property (nonatomic, assign)CGFloat height;
@property (nonatomic, assign)CGFloat centerX;
@property (nonatomic, assign)CGFloat centerY;
@property (nonatomic, assign)CGSize size;
@end
#UIView+LBExtension.m-------------------------
#import "UIView+LBExtension.h"
@implementation UIView (LBExtension)
- (void)setX:(CGFloat)x
{
CGRect frame = self.frame;
frame.origin.x = x;
self.frame = frame;
}
- (CGFloat)x
{
return self.frame.origin.x;
}
- (void)setWidth:(CGFloat)width
{
CGRect frame = self.frame;
frame.size.width = width;
self.frame = frame;
}
- (CGFloat)width
{
return self.frame.size.width;
}
- (void)setHeight:(CGFloat)height
{
CGRect frame = self.frame;
frame.size.height = height;
self.frame= frame;
}
- (CGFloat)height
{
return self.frame.size.height;
}
- (void)setSize:(CGSize)size
{
CGRect frame = self.frame;
frame.size = size;
self.frame = frame;
}
- (CGSize)size
{
return self.frame.size;
}
- (void)setCenterX:(CGFloat)centerX
{
CGPoint center = self.center;
center.x = centerX;
self.center = center;
}
- (CGFloat)centerX
{
return self.center.x;
}
- (void)setCenterY:(CGFloat)centerY
{
CGPoint center = self.center;
center.y = centerY;
self.center = center;
}
- (CGFloat)centerY
{
return self.center.y;
}
@end
代码部分完成 !