Главная iPhone Mac OS X Форум О себе

Закладки в ваших приложениях

Одним из часто употребимых элементов навигации в интерфейсе программ, построенных на базе библиотеки UIKit, является UITabBarController. Редко какое приложение может обойтись без его функциональности, позволяющей структурировать информацию по разделам и объединять однородные действия в группы, хотя способ применения данного элемента не ограничен указанными возможностями и в полной мере зависит от ваших предпочтений.

Первым местом, где можно воочию увидеть и произвести знакомство с UITabBarController, является Interface Builder.

UITabBarController в Interface Builder

UITabBarController похож на панель табов в браузере, отображающей независимые друг от друга страницы, в роли которых могут выступать другие контроллеры, включая UIViewController, UITableViewController и UINavigationController. По структуре UITabBarController напоминает рассматриваемый нами ранее UINavigationController: он также имеет отдельную панель для размещения кнопок — UITabBar (по аналогии с UINavigationBar), у каждого подчиненного элемента имеется свой объект UITabBarItem (аналог UINavigationItem), хотя функционально эти элементы разнятся от подобных им в UINavigationController.

Свойство viewControllers у UITabBarController содержит массив элементов UIViewController от объектов, привязанных к каждой из закладок UITabBar.

Tab Bar Controller Attributes

На этом месте хочу заострить ваше внимание и дать дополнительные объяснения. Для отображения элементов в каждом “табе” UITabBarController не нужно знать класс объекта, который привязан к соответствующей закладке — для этих целей используется ссылка на его UIViewController, отвечающего за отображение элемента в целом. Каждая закладка у UITabBarController представляет собой либо элемент UIViewController, либо потомок класса UIViewController (UINavigationController, UITableViewController и др.).

У объекта UIViewController имеется свойство view, отображающее содержимое объекта. Если к одной из закладок UITabBar привязан объект UIViewController, то при переходе на эту закладку будет просто показано содержимое его view. Если к закладке привязан объект UINavigationController, то его view будет отображать view от активного viewController плюс панель навигации. Такое представление значительно облегчает работу с группой разнородных элементов.

Количество закладок на Tab Bar не ограничено, но отображается только пять.

Tab Bar приложение в эмуляторе

Если общее число “табов” превышает 5 штук, то отображаются первые 4 и кнопка More, нажатие на которую показывает список оставшихся элементов.

Кнопка More и список контроллеров, не поместившихся на Tab Bar

В последнем случае можно перейти в режим редактирования и перетащить наиболее часто используемые элементы на панель Tab Bar. Вы можете ограничить список элементов, которые можно перетащить на Tab Bar, указав их в массиве customizableViewControllers программным образом. Если список пуст, то кнопка редактирования будет недоступна. По умолчанию доступны все элементы.

Изменение списка табов на Tab Bar

Для контроля над процессом редактирования списка доступных на Tab Bar элементов существуют два делегата: UITabBarControllerDelegate и UITabBarDelegate. Если вы хотите произвести какие-то дополнительные действия после или во время изменения списка “табов”, делегаты предоставляют методы, позволяющие проделать это. Но чаще всего ничего делать не приходится, а сам элемент Tab Bar представляет из себя этакий объект весь в себе, в работу которого не приходится вмешиваться.

Кнопки на панели Tab Bar являются объектами класса UITabBarItem. Каждая из них может быть инициализирована либо стандартным набором кнопок, либо вручную с указанием надписи и изображения.

Стандартный и нестандартный таб

Кроме того, каждая кнопка имеет свойство badgeValue, при помощи которого можно указать индивидуальную надпись в правом верхнем углу для индикации, например, прихода новых сообщений, окончания загрузок или другой информации. Надпись может содержать не только цифры, но и любое строковое значение.

Значок дополнительной информации рядом с табом


Загрузка внешних файлов интерфейса в программу

Основное правило, которым лучше руководствоваться при создании интерфейса любого приложения, — хранить блоки интерфейса в отдельных nib-файлах. Такое разделение уменьшает время загрузки программы и предотвращает чрезмерный расход памяти. Для случая использования в программе объекта UITabBarController это утверждение начинает иметь куда большее значение. Представьте насколько много объектов и связей может концентрироваться на каждой из закладок, и справедливость утверждения не потребует доказательств.

Поддержка разделения интерфейса по nib-файлам заложена в функциональности файлов этого формата. Для понимания работы загрузки внешних файлов интерфейсов создайте новый проект в XCode на основе шаблона “Tab Bar Application”. Этот проект уже имеет правильно инициализированный UITabBarController и два “таба”, один из которых получает свое содержимое из файла SecondView.nib.

Tab Bar Application: схема подчинения объектов в основном интерфейсе приложения

Tab Bar Application: файлы интерфейсов

Выделите объект UIViewController, связанный со второй закладкой на UITabBarController, и откройте инспектор свойств. Поле NIB Name содержит имя файла без расширения, который будет загружен в качестве содержимого этой закладки.

Поле ввода имени внешнего NIB-файла

Загружаемый файл должен удовлетворять ряду требований.

  1. Прокси-объект File’s Owner должен быть того же класса, что и класс объекта, куда загружается файл. В нашем случае это UIViewController.
  2. Все объекты верхнего уровня в интерфейсе должны быть связаны с File’s Owner при помощи outlet переменных или подчинены между собой. В нашем случае мы связываем таким образом переменную view и объект UIView на форме. Необходимость в такой обязательной привязке объясню чуть позже.

Tab Bar Application: схема подчинения объектов во внешнем файле интерфейса

Файл SecondView.nib не будет загружен в приложение до тех пор, пока пользователь не перейдет на вторую закладку. Такая функциональность достигается за счет размещения процедуры загрузки в метод loadView у UIViewController, которая скрыта от наших глаз. Но мы с вами попробуем реализовать ее самостоятельно, попутно разобравшись как все действует.

Первым делом откройте MainWindow.xib и очистите поле NIB Name у UIViewController, связанного со второй закладкой на Tab Bar. Затем присвойте ему класс secondViewController, в которого мы опишем реализацию метода loadView.

Меняем класс UIViewController

После чего сгенерируйте недостающие файлы secondViewController.m и secondViewController.h из Interface Builder вызовом Write Class Files из меню (подробности сей замысловатой процедуры описаны в Знакомство с Interface Builder. Связи между объектами).

В заголовочном файле класса объявите родительский класс для secondViewController как UIViewController, а в реализацию добавьте метод loadView со следующим содержимым:


-(void)loadView {
    [[NSBundle mainBundle] loadNibNamed:@"SecondView" owner:self options:nil];
}

Метод loadView вызывается в том случае, если происходит обращение к View Controller, а view, которым он управляет, не существует — прям-таки наш случай. В теле метода происходит загрузка файла SecondView.nib из Application Bundle.

loadNibNamed:owner:options: загружает указанный в первом параметре nib-файл и присваивает его содержимое родительскому классу, указанному в параметре owner. options чаще всего не используется и необходим для ручного создания связей между прокси-объектами, загруженными из nib-файла (за исключением File’s Owner), с существующими объектами в программе.

Метод loadNibNamed:owner:options: возвращает массив объектов верхнего уровня из загруженного nib-файла, за исключением прокси-объектов. Под объектами верхнего уровня подразумеваются объекты, не имеющие прямого подчинения к другим. Например, для нашего файла SecondView таковыми является только UIView, т.к. File’s Owner и First Responder — это прокси-объекты, а UILabel и UITextView подчинены UIView.

Схема подчинения элементов в файле SecondView

После загрузки файла интерфейса в программу ответственность за хранение и освобождение объектов, объявленных внутри него, лежит на владельце, переданном в параметре owner. После того как объект-владелец будет освобожден, необходимо освободить и все объекты, загруженные из файла.

В нашем случае единственный объявленный объект — это UIView, ссылка на который присвоена свойству view у File’s Owner. После загрузки файла интерфейса эта ссылка переходит в ведение UIViewController, привязанному ко второй закладке Tab Bar. Во время завершения работы программы UIViewController пошлет сообщение release всем своим свойствам, в том числе и view, который освободит занятую память. А view, в свою очередь, пошлет сигнал об освобождении своим “детям” — UILabel и UITextView.

Если File’s Owner содержит недостаточное количество outlet ссылок на объекты интерфейса, входящих в состав загружаемого файла, то вы можете получить к ним доступ, воспользовавшись возвращаемым методом loadNibNamed:owner:options: массивом.

Здесь есть небольшие “грабли” — при использовании подобного метода для загрузки nib-файла в Mac OS вам потребовалось бы послать сообщение release каждому элементу, входящему в состав массива, т.к. для каждого объекта, входящего в состав интерфейса, операция retain выполняется дважды: в момент связывания с File’s Owner и другими элементами, и в момент присвоения возвращаемому массиву. Но в iPhone OS эти “грабли” убрали, и можно работать с массивом без необходимости лишней операции release. Более тесно ознакомиться с этой темой можно воспользовавшись приведенными в конце статьи ссылками.

Возвращаясь к нашему UITabBarController хочу добавить, что процесс загрузки внешнего nib-файла для других типов контроллеров, входящих в состав Tab Bar, ничем не отличается от изложенного выше. Например, для случая UINavigationController схема связей между объектами в Interface Builder выглядела бы следующим образом:

Добавление UINavigationController в Tab Bar Application в качестве дополнительного таба

А класс secondViewController с функцией загрузки был бы присвоен входящему в состав UINavigationController объекту UIViewController. Либо можно воспользоваться более простым вариантом с прямым указанием файла интерфейса в поле NIB Name у UIViewController.

Создание UITabBarController программным образом

Создание UITabBarController через код не должно вызвать особых трудностей, но я все же приведу краткое описание этого процесса. Первым делом следует создать набор контроллеров, которые будут отображаться в табах Tab Bar. Один из них может выглядеть, например, следующим образом.

customViewController.h


#import <UIKit/UIKit.h>

@interface customViewController: UIViewController {
}
@end

customViewController.m


#import "customViewController.h"
@implementation customViewController

— (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        self.title = @"Tab Title";
        self.tabBarItem.image = [UIImage imageNamed:@"Tabmage.png"];
    }
    return self;
}

@end

tabBarItem не нужно инициализировать вручную через [[UITabBarItem alloc] init… Это происходит автоматически при первом обращении к свойству. Изображение для “таба” должно быть размером 30 х 30 пикселей. Для создания эффекта нажатой кнопки используется альфа слой в изображении.

Свойство view у нашего View Controller вы можете либо создать программным способом, либо загрузить из внешнего файла. Как и выбор места, где следует его инициализовать (в loadView или в конструкторе класса), эта процедура полностью лежит на ваших плечах.

Дальше нам следует создать UITabBarController в том месте, где предполагается его использовать.


tabBarController = [[UITabBarController alloc] initWithNibName:nil bundle:nil];

Так как UITabBarController имеет единственного прямого родственника в виде UIViewController, для которого единственный способ проинициализоваться это вызвать initWithNibName:bundle:, то нам выбирать не приходится. Во время инициализации создается UITabBar и автоматически позиционируется в нижней части экрана.

Создаем “табы” на основе созданного выше класса.


customViewController *viewController =
            [[[customViewController alloc] initWithNibName:nil bundle:nil] autorelease];
customNavigationController *navigationController =
            [[[customNavigationController alloc] initWithNibName:nil bundle:nil] autorelease];
customTextViewController *textViewController =
            [[[customTextViewController alloc] initWithNibName:nil bundle:nil] autorelease];

tabBarController.viewControllers = [NSArray arrayWithObjects:viewController,
                                                             navigationController,
                                                             textViewController,
                                                             nil];

Как я писал выше, свойство viewControllers содержит список всех “табов” у Tab Bar. Используя переменную selectedIndex можно задать номер выделенной вкладки при первом отображении UITabBarController (отсчет начинается с нуля). Кроме этой переменной, значение выделенного “таба” хранится в свойстве selectedViewController.

Массив customizableViewControllers содержит список “табов”, которые отображаются при редактировании Tab Bar (нажатии на кнопку Edit при нахождении на закладку More). Этот массив инициализируется значениями из viewControllers. То есть по умолчанию можно менять список табов, используя все контроллеры включенные в состав Tab Bar.

Последнее, что следует сделать, показать TabBarController в программе. Например, так:


[window addSubview:tabBarController.view];

Дополнительная информация:

Комментарий

Комментарии

mcmx 27.09.2020 0:00

Немного offtopic:

initHighlightingOnLoad(‘html’, ‘css’, ‘python’); — немнго не то :) initHighlightingOnLoad(‘objc’, ‘python’); — самое то будет :)

ответить
somebody 30.09.2020 5:56

А как сделать, чтобы можно было поменять текущий UIViewController? Задача стоит следующий образом. Внизу есть таббар, вверху — навбар, ровно так, как нарисовано на последнем рисунке. Но при это текущее окно содержит кнопку, при нажатии на которую я хочу загрузить совершенно новый UIViewController, не перечисленный в таббаре внизу. Я перебрал кучу всего, но так и не удалось достигнуть этого поведения. Единственное что я знаю будет гарантированно работать — это все перевести на навбар и создавать таббар внизу ручками в каждом окне. Но такой подход очень не нравится

ответить
Evgeniy Krysanov 30.09.2020 12:37

Если я правильно понял, то нужно загрузить новый UIViewController в список табов? Вариант следующий:

  1. Загружаешь в переменную интерфейс этого view controller’а из nib-файла с помощью [[someViewController alloc] initWithNibName:@"somenib" bundle:nil] или генерируешь вручную — как тебе будет удобнее. Также не забудь об обязательном добавлении элемента UITabBarItem

  2. Добавляешь эту переменную в список табов viewControllers

ответить
somebody 30.09.2020 13:08

Ну не совсем, хочется более сложного Хочется добавить новый контроллер, не в табы, а просто поменять текущий UIViewController. Ну или хотя бы пусть в табы, но так, чтобы собственно закладки не появлялось, а просто появлялся другой UIViewController

ответить
Evgeniy Krysanov 30.09.2020 14:46

Тогда попробуй то же самое, что я написал выше, только во втором пункте ЗАМЕНИ желаемый таб из массива viewControllers на свой UIViewController.

ответить
Alexander 21.11.2020 19:12

ошибочко…

customViewController.m

#import “tableViewController.h” @implementation tableViewController

ответить
Evgeniy Krysanov 23.11.2020 23:23

Спасибо, исправил.

ответить

Форма комментирования для «Закладки в ваших приложениях»

Обязательное поле. Не больше 30 символов.

Обязательное поле

captcha image Пожалуйста, введите символы, которые вы видите на изображении

Карта Сайта