Кастомный UITabBar

в 8:59, , рубрики: ios development, objective-c, разработка под iOS, метки: ,

В ходе разработки одного приложения столкнулся с проблемой: стандартный iOS-овский tabbar очень плохо поддается кастомизации. Невозможно легким движением руки поставить цветные иконки для табов, ни поменять бэкграунд как самого таб-бара, так и отдельных табов в различных состояниях (активен, не активен).
Тем не менее, задача стояла четко: внедрить имеющийся дизайн, который категорически не состыковывался с накладываемыми ограничениями.


В целом, проблему можно разделить на 3 этапа:

  1. Цветные иконки.
  2. Разный бэкграунд в различных состояниях табов.
  3. Кастомный бэкграунд самого таб-бара

С иконками проблема состоит в следующем: свойство image класса UITabbar на самом деле не меняет иконку, как было бы очевидней, а всего лишь использует наше изображение в качестве маски для иконки (подробней можно почитать здесь). Такая ситуация меня естественно не устраивала. Эта проблема решилась быстрее и легче всего.
Для решения оставшихся двух пришлось включать голову (поскольку UITabBarItem не является наследником UIView и поэтому не имеет почти никаких полезных свойств) — попытка откопать что-то в интернете не возымела успеха — большинство советов сводилось к «напиши свой собственный таб-бар с блэкджеком и шлюхами».

В итоге был написан простенький класс:

@interface KidsReviewTabBarController : UITabBarController <UITabBarControllerDelegate>

@end

@implementation KidsReviewTabBarController
{
	UIImageView *_selection;
}

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

- (void)initTabBar
{
    //Загружаем изображения для иконок
	UIImage *firstImage = [UIImage imageNamed:@"first_icon.png"];
	UIImage *secondImage = [UIImage imageNamed:@"second_icon.png"];
	UIImage *thirdImage = [UIImage imageNamed:@"third_icon.png"];

	UIImage *firstImageSelected = [UIImage imageNamed:@"first_icon_selected.png"];
	UIImage *secondImageSelected = [UIImage imageNamed:@"second_icon_selected.png"];
	UIImage *thirdImageSelected = [UIImage imageNamed:@"third_icon_selected.png"];

    //Получаем элементы таб-бара
	UITabBarItem *firstItem = [self.tabBar.items objectAtIndex:0];
	UITabBarItem *secondItem = [self.tabBar.items objectAtIndex:1];
	UITabBarItem *thirdItem = [self.tabBar.items objectAtIndex:2];

    //Устанавливаем иконки для каждого из табов
	[firstItem setFinishedSelectedImage:firstImageSelected withFinishedUnselectedImage:firstImage];
	[secondItem setFinishedSelectedImage:secondImageSelected withFinishedUnselectedImage:secondImage];
	[thirdItem setFinishedSelectedImage:thirdImageSelected withFinishedUnselectedImage:thirdImage];

    //Кастомизируем бэкграунд таб-бара
	UIImage *backgroundImage = [UIImage imageNamed:@"tabbar_background.png"];
	UIImageView *imageView = [[UIImageView alloc] initWithImage:backgroundImage];
	CGRect frame = imageView.frame;
	frame.size.width = self.tabBar.frame.size.width;
	imageView.frame = frame;
	[[self tabBar] insertSubview:imageView atIndex:1];

    //Инициалиизируем бэкграунд иконки и устанавливаем его для первого элемента таб-бара
    //Это необходимо поскольку метод tabBarController:didSelectViewController: не вызывается при загрузке 
    //приложения (когда первый элемент должен быть активным)
	_selection = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"tabbar_selection.png"]];
	[[self buttonByIndex:0] insertSubview:_selection atIndex:0];
}

//Реализуем метод протокола UITabBarControllerDelegate (вызывается после выбора таба)
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
    //получаем UIView, содержащий необходимый нам элемент таб-бара, удаляем бэкграунд с предыдущего таба и 
    //устанавливаем на текущий
	UIView *selectedButton = [self buttonByIndex:tabBarController.selectedIndex];
	[_selection removeFromSuperview];
	[selectedButton insertSubview:_selection atIndex:1];
}

- (UIView *) buttonByIndex:(int)index
{
	NSMutableArray *buttons = [[NSMutableArray alloc] init];
	for (UIControl* btn in self.tabBar.subviews)
	{
		if ([btn isKindOfClass:[UIControl class]])
		{
			[buttons addObject:btn];
		}
	}

	return [buttons objectAtIndex:(NSUInteger) index];
}

Для решения задачи не обязательно создавать наследника UITabBarController как это сделал я, достаточно реализовать в каком-либо классе протокол UITabBarControllerDelegate и там отслеживать состояние таб-бара, ну и где-нибудь инициализировать иконки и бэкграунд.
Мне был более удобен вариант с наследованием от UITabBarController, поскольку шрифты и надписи менять не было необходимости и их поведение по дефолту меня полностью устраивало.

P.S. Возможно, пост получился несколько сумбурным. Если что-то непонятно — пишите.

Автор: afterwhy


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js