UITabBarControllerはタブバーのコントローラーですが、
タブバーは細かいデザインの調整を行う場合はUITabBarControllerのサブクラスを作って実装するパターンが多いと思います。

一つのStoryBoardに全てのタブの参照がある場合はInterface Builderなどでもある程度はデザインを設定できますが、StoryboardをViewController毎やタブ毎等に分けていた場合はInterface BuilderだけだとIBOutletを結べなくなります。

運用的にはタブやViewControllerでstoryboardファイルを分けた方が扱いやすいと感じるので、タブを関連付けるにはUITabBarControllerのサブクラスを作ってコードで結んでいく必要があります。

しかし、UITabBarControllerをそのまま実装すると、UITabItemのテキスト、アイコン…等意外とややこしいです。

タブバーの構成要素は以下のような感じですね。

  • タブバー背景画像
  • 選択してるタブの背景画像
  • 各タブのアイコン
  • 各タブのテキスト

実際にやることとしては以下のようになりますね。

  1. self.viewControllers に 各タブTopのViewControllerを入れる
  2. self.tabBar.items を走査して、各タブ(UITabBarItem)にタイトルやアイコンを設定
  3. タブバー全体の細かい調整

これを直に書くとforループ等がでてきてしまい余りキレイにはならないと思います。

- (void)setupTabs {
	// 1. 各タブのViewControllerを設定
    self.viewControllers = @[
        self.tabFactory.calendarNavigationController,
        self.tabFactory.graphNavigationController,
        self.tabFactory.reporterNavigationController,
        self.tabFactory.karadaNoteNavigationController,
        self.tabFactory.settingNavigationController,
    ];
	// 2. TabBarItemにタイトルなどの設定
    NSArray *tabBarItems = [[self tabBar] items];
    for (NSUInteger i = 0; i < [tabBarItems count]; i++) {
        MenuTabType menuTabType = (MenuTabType)i;
        UITabBarItem *tabBarItem = tabBarItems[i];
        tabBarItem.title = [self tabItemTitle:menuTabType];
        UIImage *tabBarItemSelectedIcon = [self selectedTabItemImage:menuTabType];
        UIImage *tabBarItemUnselectedIcon = [self unselectedTabItemImage:menuTabType];
        [self setTabBarItem:tabBarItem tabBarItemSelectedIcon:tabBarItemSelectedIcon tabBarItemUnselectedIcon:tabBarItemUnselectedIcon];
        [self setTitleTextColorWithTabBarItem:tabBarItem];
    }
	// 3. タブバー全体の設定
    [[self tabBar] setSelectionIndicatorImage:[UIImage imageNamed:@"tab_select_image.png"]];
    [[self tabBar] setBackgroundImage:[UIImage imageNamed:@"tab_backgroundimage.png"]];
    [[self tabBar] setSelectionIndicatorImage:[UIImage imageNamed:@"tab_select_image.png"]];
}

という感じになってしまいました。

そこで、これをパターン化して解決出来るようにするRoleTabBarControllerというライブラリを書きました。

RoleTabBarController

このライブラリが扱うパターンと言うのは<UITableViewDataSource>、UITableViewのDataSourceと基本的に同じです。

$ pod try RoleTabBarController

で試すことが出来ます。

詳しくはExampleを見てもらいたいのですが、先ほどの例はUITabBarController内に全て書いていましたが、UITableViewDataSourceと同じようにModelクラスに情報を出しやすくなります。

@implementation AppTabBarController
- (AppTabBarControllerModel *)model {
    if (_model == nil) {
        _model = [[AppTabBarControllerModel alloc] init];
    }
    return _model;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.dataSource = self;
}


#pragma mark - RoleTabBarController

- (NSUInteger)numberOfRoleTabBarController:(RoleTabBarController *) tabBarController {
    return 4;// 実際にはこれもモデルに出せる
}

- (UIViewController *)roleTabBarController:(RoleTabBarController *) tabBarController viewControllerAtIndex:(NSUInteger) index {
    return [self.model viewControllerAtType:(AppTabBarControllerType)index];
}

- (RoleTabBarItemObject *)roleTabBarController:(RoleTabBarController *) tabBarController tabBarItemObjectAtIndex:(NSUInteger) index {
    return [self.model tabBarItemObjectAtIndex:index];
}
@end

モデルクラスの方は、indexに応じたViewControllerやUITabBarItemの設定オブジェクトを返すだけですね。

 

typedef NS_ENUM(NSUInteger, AppTabBarControllerType) {
    AppTabBarControllerType_MAIN,
    AppTabBarControllerType_CALENDAR,
    AppTabBarControllerType_POST,
    AppTabBarControllerType_RANKING,
};

@implementation AppTabBarControllerModel {
}
- (UIViewController *)viewControllerAtType:(AppTabBarControllerType) type {
    switch (type) {
        case AppTabBarControllerType_MAIN:
            return [RoleViewController viewController:@"main"];
        case AppTabBarControllerType_CALENDAR:
            return [RoleViewController viewController:@"calendar"];
        case AppTabBarControllerType_POST:
            return [RoleViewController viewController:@"post"];
        case AppTabBarControllerType_RANKING:
            return [RoleViewController viewController:@"ranking"];
    }
    return nil;
}

- (RoleTabBarItemObject *)tabBarItemObjectAtType:(AppTabBarControllerType) type {
    switch (type) {
        case AppTabBarControllerType_MAIN:
            return [[RoleTabBarItemObject alloc] initWithTitle:@"MAIN" selectedImage:nil unselectedImage:nil];
        case AppTabBarControllerType_CALENDAR:
            return [[RoleTabBarItemObject alloc] initWithTitle:@"CALENDAR" selectedImage:nil unselectedImage:nil];
        case AppTabBarControllerType_POST:
            return [[RoleTabBarItemObject alloc] initWithTitle:@"POST" selectedImage:nil unselectedImage:nil];
        case AppTabBarControllerType_RANKING:
            return [[RoleTabBarItemObject alloc] initWithTitle:@"RANKING" selectedImage:nil unselectedImage:nil];
    }
    return nil;
}

- (RoleTabBarItemObject *)tabBarItemObjectAtIndex:(NSUInteger) index {
    return [self tabBarItemObjectAtType:(AppTabBarControllerType)index];
}
@end

RoleTabBarItemObjectはTabBarItemのタイトルと選択時のアイコン、非選択時のアイコンを設定出来るオブジェクトです。

基本的なデザインならこれで足りますが、TabBarItemのテキスト位置などを調整したい場合は、

- (void)roleTabBarController:(RoleTabBarController *) tabBarController willShowTabBar:(UITabBarItem *) tabBarItem atIndex:(NSUInteger) index;

 を実装して、そこでUITabBarItemを更新するようにするといいです。

RoleTabBarControllerは<UITableViewDataSource>のパターンを使ってUITabBarControllerを実装するパターンを提供するライブラリです。

ライブラリも100行行かない程度のシンプルなものなので、試してみるといいかもしれません。

Post Navigation