UITabBarControllerはタブバーのコントローラーですが、
タブバーは細かいデザインの調整を行う場合はUITabBarControllerのサブクラスを作って実装するパターンが多いと思います。
一つのStoryBoardに全てのタブの参照がある場合はInterface Builderなどでもある程度はデザインを設定できますが、StoryboardをViewController毎やタブ毎等に分けていた場合はInterface BuilderだけだとIBOutletを結べなくなります。
運用的にはタブやViewControllerでstoryboardファイルを分けた方が扱いやすいと感じるので、タブを関連付けるにはUITabBarControllerのサブクラスを作ってコードで結んでいく必要があります。
- Storyboardを1画面ごとに分割した話 – 24/7 twenty-four seven
- Q. StoryboardとXIBはどう使い分ければいいんだろうか? – Qiita
- Pilky.me – Using Storyboards
しかし、UITabBarControllerをそのまま実装すると、UITabItemのテキスト、アイコン…等意外とややこしいです。
タブバーの構成要素は以下のような感じですね。
- タブバー背景画像
- 選択してるタブの背景画像
- 各タブのアイコン
- 各タブのテキスト
実際にやることとしては以下のようになりますね。
- self.viewControllers に 各タブTopのViewControllerを入れる
- self.tabBar.items を走査して、各タブ(UITabBarItem)にタイトルやアイコンを設定
- タブバー全体の細かい調整
これを直に書くと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と基本的に同じです。
で試すことが出来ます。
詳しくは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行行かない程度のシンプルなものなので、試してみるといいかもしれません。