iOS开发之自定义TabBarController-present(模态)出控制器

点击TabBar present出控制器.gif

�如上图这种点击TabBar中间Button时present(模态)出控制器,而点击TabBar其它的Button则是苹果默认的直接切换视图的方式,现在很多APP都在使用,特别是直播APP,基本都把直播端模块放在这,最近在仿喵播时正好做到这,�就想在网上找现成的框架,结果一直没找到能满足这种需求的框架,就自己动手写了一个,没有仔细打磨算不上框架,但绝对简单好用!

自定义TabBarController实现原理很简单,无非是用自定义的Button替换掉苹果自带的TabBar上的Button,一般目的是想TabBar上方显示按钮图片,下面是文字;也就是每个Button下面有一个Label。实现这些的重点是计算ButtonLabel的宽度和X坐标值。

下面是JFTabBarController代码实现:

1、 新建三个类:

  • JFTabBar继承自UIview
  • JFTabBarButton继承自UIButton
  • JFTabBarController继承自UITabBarController

2、具体代码:

  • JFTabBar.h
#import <UIKit/UIKit.h>

@class JFTabBar;

@protocol JFTabBarDelegate <NSObject>

@optional

- (void)tabBar:(JFTabBar *)tabBar didSelectedButtonFrom:(int)from to:(int)to;

@end

@interface JFTabBar : UIView

- (void)addTabBarButtonWithItem:(UITabBarItem *)item;

- (void)customTheMiddleButtonWithImageName:(NSString *)imageName;

@property (nonatomic, weak) id<JFTabBarDelegate> delegate;

@end
  • JFTabBar.m
#import "JFTabBar.h"

#import "JFTabBarButton.h"

//中间按钮被点击的通知
//此通知需要在JFTabBarController中监听
static NSString * const JFTabBarClickMiddleButtonDidNotification = @"JFTabBarClickMiddleButtonDidNotification";

@interface JFTabBar(){
    CGFloat _buttonW;
}

@property (nonatomic, weak) JFTabBarButton *selectedButton;

@property (nonatomic, strong) UIButton *middleButton;

@end

@implementation JFTabBar

- (void)addTabBarButtonWithItem:(UITabBarItem *)item {
    JFTabBarButton *button = [[JFTabBarButton alloc] init];
    [self addSubview:button];
    button.item = item;
    [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchDown];
    if (self.subviews.count == 1) {
        [self buttonClick:button];
    }
}

- (void)buttonClick:(JFTabBarButton *)button {
    if ([self.delegate respondsToSelector:@selector(tabBar:didSelectedButtonFrom:to:)]) {
        [self.delegate tabBar:self didSelectedButtonFrom:(int)self.selectedButton.tag to:(int)button.tag];
    }
    self.selectedButton.selected = NO;
    button.selected = YES;
    self.selectedButton = button;
}

/**
 *  self.subviews.count + 1 这里加1是将button的width按三个button计算,实际上是两个button(如果不想present就不需要加1,下面两个if判断也可以去掉)
 *  两个if判断:1、是将button的x坐标按三个button计算;2、tag值回归正常,不然无法正常切换控制器
 *  如你需要添加更多的控制器,这里就需要你自己计算,然后修改以上两点
 */
- (void)layoutSubviews {
    [super layoutSubviews];
    CGFloat buttonH = self.frame.size.height;
    _buttonW = self.frame.size.width / (self.subviews.count + 1);
    CGFloat buttonY = 0;
    for (int index = 0; index < self.subviews.count; index++) {
        JFTabBarButton *button = self.subviews[index];
        if (index == 1) index ++;
        CGFloat buttonX = index * _buttonW;
        button.frame = CGRectMake(buttonX, buttonY, _buttonW, buttonH);
        if (index == 2) index --;
        button.tag = index;
    }
    [self customTheMiddleButtonWithImageName:@"toolbar_live"];
}

//自定义中间button按钮
- (void)customTheMiddleButtonWithImageName:(NSString *)imageName {
//  将Button居中
    CGFloat middleButtonX = self.center.x - _buttonW / 2;
    _middleButton = [[UIButton alloc] initWithFrame:CGRectMake(middleButtonX, 0, _buttonW, self.frame.size.height)];
    [_middleButton setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
    [_middleButton addTarget:self action:@selector(didClickedMiddleButton:) forControlEvents:UIControlEventTouchDown];
    [self addSubview:_middleButton];
}

- (void)didClickedMiddleButton:(UIButton *)sender {
    //注册中间button被点击通知
    [[NSNotificationCenter defaultCenter] postNotificationName:JFTabBarClickMiddleButtonDidNotification object:self];
}
@end
  • JFTabBarButton.h
#import <UIKit/UIKit.h>

@interface JFTabBarButton : UIButton

@property (nonatomic, strong) UITabBarItem *item;

@end
  • JFTabBarButton.m
static const double JFTabBarButtonImageRatio = 0.8;
#import "JFTabBarButton.h"

@implementation JFTabBarButton

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.imageView.contentMode = UIViewContentModeCenter;
        self.titleLabel.textAlignment = NSTextAlignmentCenter;
        self.titleLabel.font = [UIFont systemFontOfSize:11];
        [self setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
    }
    return self;
}

- (void)setHighlighted:(BOOL)highlighted {}

- (CGRect)imageRectForContentRect:(CGRect)contentRect{
    CGFloat imageW = contentRect.size.width;
    CGFloat imageH = contentRect.size.height * JFTabBarButtonImageRatio;
    return CGRectMake(0, 10, imageW, imageH);
}

//Button下需要文字时打开
//- (CGRect)titleRectForContentRect:(CGRect)contentRect {
//    CGFloat titleY = contentRect.size.height * JFTabBarButtonImageRatio;
//    CGFloat titleW = contentRect.size.width;
//    CGFloat titleH = contentRect.size.height - titleY;
//    return CGRectMake(0, titleY, titleW, titleH);
//}

- (void)setItem:(UITabBarItem *)item {
    _item = item;
    //Button下需要文字时打开
//    [item addObserver:self forKeyPath:@"title" options:0 context:nil];
    [item addObserver:self forKeyPath:@"image" options:0 context:nil];
    [item addObserver:self forKeyPath:@"selectedImage" options:0 context:nil];
    
    [self observeValueForKeyPath:nil ofObject:nil change:nil context:nil];
}

- (void)dealloc {
    //Button下需要文字时打开
//    [self.item removeObserver:self forKeyPath:@"title"];
    [self.item removeObserver:self forKeyPath:@"image"];
    [self.item removeObserver:self forKeyPath:@"selectedImage"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
//    [self setTitle:self.item.title forState:UIControlStateSelected];
//    [self setTitle:self.item.title forState:UIControlStateNormal];
    [self setImage:self.item.image forState:UIControlStateNormal];
    [self setImage:self.item.selectedImage forState:UIControlStateSelected];
}

@end
  • JFTabBarController.h

- (void)addAllChildViewControllers中添加除中间那个你需要present(模态)出的控制器

#import <UIKit/UIKit.h>

extern NSString * const JFTabBarClickMiddleButtonDidNotification;

@interface JFTabBarController : UITabBarController

@end
  • JFTabBarController.m
#import "JFTabBarController.h"

#import "JFTabBar.h"
#import "JFDisplayViewController.h"
#import "JFMeViewController.h"
#import "JFLiveViewController.h"

@interface JFTabBarController ()<JFTabBarDelegate>

@property (nonatomic, weak) JFTabBar *customTabBar;

@end

@implementation JFTabBarController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self customTabBarUI];
//  添加子控制器
    [self addAllChildViewControllers];

//  监听中间按钮点击事件
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(presentLiveController) name:@"JFTabBarClickMiddleButtonDidNotification" object:nil];
}
//  移除原生的TabBar
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    for (UIView *childView in self.tabBar.subviews) {
        if ([childView isKindOfClass:[UIControl class]]) {
            [childView removeFromSuperview];
        }
    }
}

- (void)customTabBarUI {
    JFTabBar *customTabBar = [[JFTabBar alloc] init];
    customTabBar.frame = self.tabBar.bounds;
    customTabBar.delegate = self;
    [self.tabBar addSubview:customTabBar];
    self.customTabBar = customTabBar;
}

- (void)tabBar:(JFTabBar *)tabBar didSelectedButtonFrom:(int)from to:(int)to{
    self.selectedIndex = to;
}

//   下面换成你自己需要添加的控制器就好,只添加除中间需要present(模态)出的控制器。
- (void)addAllChildViewControllers {
    JFDisplayViewController *displayVC = [[JFDisplayViewController alloc] init];
    [self addChildViewController:displayVC title:@"视频" imageName:@"toolbar_home" selectedImageName:@"toolbar_home_sel"];
    JFMeViewController *meVC = [[JFMeViewController alloc] init];
    [self addChildViewController:meVC title:@"个人" imageName:@"toolbar_me" selectedImageName:@"toolbar_me_sel"];
}

- (void)addChildViewController:(UIViewController *)childViewController title:(NSString *)title imageName:(NSString *)imageName selectedImageName:(NSString *)selectedImageName {
    childViewController.title = title;
    childViewController.tabBarItem.image = [UIImage imageNamed:imageName];
    childViewController.tabBarItem.selectedImage = [[UIImage imageNamed:selectedImageName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:childViewController];
    [self addChildViewController:navigationController];
    [self.customTabBar addTabBarButtonWithItem:childViewController.tabBarItem];
}

//接收到点击中间按钮的通知,present(模态)出控制器
- (void)presentLiveController {
    JFLiveViewController *jfLiveVC = [[JFLiveViewController alloc] init];
    [self presentViewController:jfLiveVC animated:YES completion:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

//  移除通知
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end

说了这么多,如果你也只是需要三个控制器,其实你只需要做这些:
1、下载JFTabBarController源码,解压将JFTabBarController文件夹中文件直接拖入你的xcode项目工程中。
2、在AppDelegate.m文件中JFTabBarController设为window的根控制器。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    self.window.rootViewController = [[JFTabBarController alloc] init];
    [self.window makeKeyAndVisible];
    return YES;
}

3、重写JFTabBarcontroller.m文件中-(void)addAllChildViewControllers函数,添加子控制器
4、JFTabBarClickMiddleButtonDidNotification通知,
重写JFTabBarcontroller.m文件中- (void)presentLiveController方法。
你需要做的就这四步!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容