玩转Tabbar(一) - APP主页框架搭建(一)

版本记录

版本号 时间
V1.0 2017.03.29 星期三

前言

很多APP都是标签控制器和导航控制器嵌套在一起,这样的架构是APP的经典架构。下面就说一下这个框架的实现,让我们一起看一下在Tabbar上我们都可以玩什么。


计划目标

标签控制器嵌套导航控制器实现APP基本架构。这里我写了两种,一种是经典的标签控制器,另外一种是自定义标签控制器。

类QQ框架

和下面这个自定义tabBar

类微博框架

以及下面这个自定义按钮超过tabBar高度的特殊情况

具有特殊按钮的自定义tabBar

目标实现

1、类QQ框架实现

我们先看一下都写了哪些类,这里只是写的一个demo,并没有按照正常的项目那样组织代码结构,先看一下。

组织结构

这里标签分了三部分,分别是主页(DDMainInfoVC),发现(DiscoverVC),我的(DDMineVC)。

不多说了,我就直接上代码了。

AppDelegate.m文件

#import "AppDelegate.h"
#import "DDMainVC.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];    
    DDMainVC *mainVC = [[DDMainVC alloc] init];
    self.window.rootViewController = mainVC;
    [self.window makeKeyAndVisible];

    return YES;
}

DDMainVC.h文件

#import <UIKit/UIKit.h>

@interface DDMainVC : UITabBarController

@end

DDMainVC.m文件

#import "DDMainVC.h"
#import "DDNavigationVC.h"
#import "DDMainInfoVC.h"
#import "DiscoverVC.h"
#import "DDMineVC.h"

@interface DDMainVC ()

@end

@implementation DDMainVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    
    UIViewController *mainVC = [self getClassName:@"DDMainInfoVC" nameTitle:@"首页" imageName:@"home_line"];
    UIViewController *discoverVC = [self getClassName:@"DiscoverVC" nameTitle:@"发现" imageName:@"discover_line"];
    UIViewController *mineVC = [self getClassName:@"DDMineVC" nameTitle:@"我的" imageName:@"my_line"];
    
    self.viewControllers = @[mainVC, discoverVC, mineVC];
}


#pragma mark - Object Proivate Function

- (UIViewController *)getClassName: (NSString *)classStr nameTitle: (NSString *)nameTitle imageName: (NSString *)imageName
{
    Class cls = NSClassFromString(classStr);
    UIViewController *vc = [[cls alloc] init];
    NSAssert([vc isKindOfClass:[UIViewController class]], @"%@类名写错了",classStr);
    vc.title = nameTitle;
    vc.tabBarItem.image = [UIImage imageNamed:imageName];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];

    return nav;
}

@end


其他的几个控制器就没什么写的了,我只是在里面不同的控制器的View设置成了不同的颜色。如下图所示。

细节显示

ps:这里图标寓意对应的不是很好,大家凑合着看吧。

2、类WeiBo框架实现

  • 先看一下Swift实现代码组织结构
类微博框架Swift实现

直接看代码

JJWbTabBarController.swift文件

import UIKit

class JJWbTabBarController: UITabBarController {

    //MARK - Override Base Function
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tabBar = JJWeiboTabBar()
        setValue(tabBar, forKey: "tabBar")
        
        addChildViewController(viewController: JJHomeVC(), title: "首页", imageName: "tabbar_home")
        addChildViewController(viewController: JJMessageVC(), title: "消息", imageName: "tabbar_message_center")
        addChildViewController(viewController: JJDiscoverVC(), title: "发现", imageName:  "tabbar_discover")
        addChildViewController(viewController: JJProfileVC(), title: "我", imageName: "tabbar_profile")
    }
    
    //MARK - Object Private Function
    
    private func addChildViewController(viewController: UIViewController, title: String, imageName: String){
        
        viewController.title = title
        viewController.tabBarItem.image = UIImage(named: imageName);
        viewController.tabBarItem.selectedImage = UIImage(named: "\(imageName)_selected")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
        viewController.tabBarItem .setTitleTextAttributes([NSForegroundColorAttributeName : UIColor.orange], for: UIControlState.selected)
        let vcNav = JJWbNavigationVC(rootViewController: viewController)
        addChildViewController(vcNav)
        
    }

}

AppDelegate.swift文件

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = JJWbTabBarController()
        window?.makeKeyAndVisible()
        
        return true
    }
}
JJWeiboTabBar.swift文件

import UIKit

class JJWeiboTabBar: UITabBar {

    //重写父类方法初始化
    override init(frame: CGRect){
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        let tabBarButtonWidth = UIScreen.main.bounds.size.width * 0.2
        var index : CGFloat = 0.0
        
        for value in subviews {
            if value.isKind(of: NSClassFromString("UITabBarButton")!){
                value.frame.size.width = tabBarButtonWidth;
                value.frame.origin.x = index * tabBarButtonWidth
                index += 1
            if index == 2 {
                index += 1
            }
          }
        }
        
        composeButton.center.x = UIScreen.main.bounds.size.width * 0.5
        composeButton.center.y = self.bounds.size.height * 0.5
    }
    
    // Object Private Function
    
    private func setupUI(){
    
        addSubview(composeButton)
    }
    
    @objc private func composeButtonDidClick(){
    
        print("composeButton is clicked")
    }
    
    // lazy load
    
    lazy var composeButton : UIButton = {
        let button = UIButton()
        button.setImage(UIImage(named:"tabbar_compose_icon_add"), for: UIControlState.normal)
        button.setImage(UIImage(named:"tabbar_compose_icon_add_highlighted"), for: UIControlState.highlighted)
        button.setBackgroundImage(UIImage(named:"tabbar_compose_button"), for: UIControlState.normal)
        button.setBackgroundImage(UIImage(named:"tabbar_compose_button_highlighted"), for: UIControlState.highlighted)
        button.addTarget(self, action: #selector(composeButtonDidClick), for: UIControlEvents.touchUpInside)
        button.sizeToFit()
        return button;
    }()

}

直接运行,可见效果如目标一样,这里只贴出一张特写图片。

细节效果图
  • 再看一下OC代码的实现

先看组织结构

类微博框架OC实现

直接看代码

AppDelegate.m文件

#import "AppDelegate.h"
#import "JJWbTabBarVC.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


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

JJTabBar.m 文件

#import "JJTabBar.h"

@interface JJTabBar ()

@property (nonatomic, strong) UIButton *composeButton;

@end


@implementation JJTabBar

#pragma mark - Override Base Function

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self setupUI];
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    NSInteger tabBarButtonWidth = [UIScreen mainScreen].bounds.size.width * 0.2;
    NSInteger index = 0;
    
    for (UIView *value in self.subviews) {
        if ([value isKindOfClass:[NSClassFromString(@"UITabBarButton") class]]) {
            
            CGRect tabBarButtonFrame = value.frame;
            
            tabBarButtonFrame.origin.x = tabBarButtonWidth * index;
            tabBarButtonFrame.size.width = tabBarButtonWidth;
            value.frame = tabBarButtonFrame;
            index++;
            if (index == 2) {
                index++;
            }
            
        }
    }

    CGPoint composeButtonCenter = self.composeButton.center;
    composeButtonCenter.x = [UIScreen mainScreen].bounds.size.width * 0.5;
    composeButtonCenter.y = self.bounds.size.height * 0.5;
    self.composeButton.center = composeButtonCenter;
    
}


#pragma mark - Object Base Function

- (void)setupUI
{
    UIButton *button = [[UIButton alloc] init];
    [button setImage:[UIImage imageNamed:@"tabbar_compose_icon_add"] forState:UIControlStateNormal];
    [button setImage:[UIImage imageNamed:@"tabbar_compose_icon_add_highlighted"] forState:UIControlStateSelected];
    [button setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button"] forState:UIControlStateNormal];
    [button setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button_highlighted"] forState:UIControlStateSelected];
    [button addTarget:self action: @selector(composeButtonDidCompose) forControlEvents:UIControlEventTouchUpInside];
    [button sizeToFit];
    self.composeButton = button;
    [self addSubview:self.composeButton];
}

- (void)composeButtonDidCompose
{
    NSLog(@"compose Button did clicked");
}

@end
JJWbTabBarVC.m文件

#import "JJWbTabBarVC.h"
#import "JJWbNavigationVC.h"
#import "JJHomeVC.h"
#import "JJMessageVC.h"
#import "JJDiscoverVC.h"
#import "JJProfileVC.h"
#import "JJTabBar.h"

@interface JJWbTabBarVC ()

@property (nonatomic, strong) JJTabBar *customTabBar;

@end

@implementation JJWbTabBarVC


#pragma mark - Override Base Function

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setupUI];
    
}


#pragma mark - Object Private Function

- (void)setupUI
{
    self.customTabBar = [[JJTabBar alloc] init];
    [self setValue:self.customTabBar forKey:@"tabBar"];
    
    [self addChildVCWithController:@"JJHomeVC" title:@"首页" imageName:@"tabbar_home"];
    [self addChildVCWithController:@"JJMessageVC" title:@"消息" imageName:@"tabbar_message_center"];
    [self addChildVCWithController:@"JJDiscoverVC" title:@"发现" imageName:@"tabbar_discover"];
    [self addChildVCWithController:@"JJProfileVC" title:@"我" imageName:@"tabbar_profile"];

}

- (void)addChildVCWithController: (NSString *)vcName title: (NSString *)title imageName: (NSString *)imageName
{
    Class cls = NSClassFromString(vcName);
    UIViewController *vc = [[cls alloc] init];
    JJWbNavigationVC *nav = [[JJWbNavigationVC alloc] initWithRootViewController:vc];
    if ([vc isKindOfClass:[UIViewController class]]) {
        vc.title = title;
        vc.tabBarItem.image = [UIImage imageNamed:imageName];
        vc.tabBarItem.selectedImage = [[UIImage imageNamed:[NSString stringWithFormat:@"%@_selected",imageName]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
        [vc.tabBarItem setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor orangeColor],NSForegroundColorAttributeName, nil] forState:UIControlStateSelected];
    }
    [self addChildViewController:nav];
}


@end

具体结果同上这里就不贴出来了。

3、具有特殊按钮的自定义tabBar

我这里说的特殊,是因为其中有的按钮的高度超过了tabBar的高度,一旦超过了其高度,就不是视图那样简单了,超过了父控件的大小,就要处理响应的问题,都需要进行hitTest测试。我们只要在上面类QQ框架上进行修改即可。

  • swift的实现
JJWeiboTabBar.swift  修改文件

    @objc private func composeButtonDidClick(){
    
        print("composeButton is clicked")
    }
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        
        if self.isUserInteractionEnabled == false || self.isHidden == true || self.alpha == 0{
            return nil;
        }

        if point.y >= self.composeButton.frame.origin.y && point.y <= self.composeButton.frame.origin.y + self.composeButton.bounds.height && point.x >= self.composeButton.frame.origin.x && point.x <= self.composeButton.frame.origin.x + self.composeButton.bounds.width{
            return self.composeButton
        }
            
        else {
            return super.hitTest(point, with: event)
        }
    }

实现结果如目标中所示,就不贴图了。

  • OC的实现
    还是看代码
JJTabBar.m 修改文件


- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (CGRectContainsPoint(self.composeButton.frame, point)) {
        return self.composeButton;
    }
    else {    
        return [super hitTest:point withEvent:event];
    }
}


具体结果还是和目标那里一样,就不贴图了。


后记

谢谢亲人朋友和喜欢我的人对我的支持,还有就是有什么写的不对的地方,希望多多指正。

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

推荐阅读更多精彩内容