Статьи

Контроллеры навигации и просмотр иерархий контроллеров

Контроллеры навигации являются одним из основных инструментов для представления нескольких экранов контента с помощью iOS SDK. Эта статья научит вас, как это сделать!


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

Как и любой другой подкласс UIViewController , контроллер навигации управляет экземпляром UIView . Представление контроллера навигации управляет несколькими подпредставлениями, включая панель навигации вверху, представление, содержащее пользовательский контент, и дополнительную панель инструментов внизу.

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

В этой статье мы создадим новое приложение для iOS, чтобы познакомиться с классом UINavigationController . Вы заметите, что комбинация контроллера навигации и стека контроллеров представления (таблицы) является элегантным и мощным решением.

В дополнение к классу UINavigationController я также расскажу о классе UITableViewController , другом подклассе UIViewController . Класс UITableViewController управляет экземпляром UITableView вместо экземпляра UIView по умолчанию. По умолчанию он принимает протоколы UITableViewDataSource и UITableViewDelegate , что сэкономит нам немало времени.


Приложение, которое мы собираемся создать, называется Library . С нашим приложением пользователи могут просматривать список авторов и просматривать книги, которые они написали. Список авторов представлен в виде таблицы.

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

Откройте Xcode, создайте новый проект, выбрав New> Project … в меню File , и выберите шаблон Single View Application из списка шаблонов iOS .

Назовите библиотеку проекта, назначьте название организации и идентификатор компании. Выберите префикс класса и установите « Устройства» на iPhone . Сообщите Xcode, где вы хотите сохранить проект, и нажмите « Создать» .

Шаблон, который мы выбрали для этого проекта, содержит класс делегата приложения ( TSPAppDelegate ), раскадровку и подкласс TSPViewController ( TSPViewController ).

Откройте TSPAppDelegate.m и посмотрите на реализацию application:didFinishLaunchingWithOptions: Его реализация должна выглядеть уже знакомой.

1
2
3
— (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    return YES;
}

Исходные файлы, предоставленные в этой статье, содержат данные, которые мы будем использовать. Вы можете найти их в папке « Ресурсы» . Папка содержит список свойств ( Books.plist ), содержащий информацию об авторах, написанных ими книгах и некоторую информацию о каждой книге, а также изображения для каждой книги, включенной в список свойств.

Перетащите папку « Ресурсы » в свой проект, чтобы добавить их в проект. Xcode покажет вам несколько вариантов, когда вы добавите папку в проект. Обязательно установите флажок Скопировать элементы в папку целевой группы (если необходимо) и не забудьте добавить файлы в целевую библиотеку .

Прежде чем продолжить, я хочу немного поговорить о списках свойств и о том, что они собой представляют. Список свойств — это не что иное, как представление графа объектов. Как мы видели ранее в этой серии, объектный граф — это группа объектов, образующих сеть через соединения или ссылки, которыми они делятся друг с другом.

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

Xcode делает просмотр списков свойств очень простым, как мы видели ранее. Выберите Books.plist из папки « Ресурсы », которую вы добавили в свой проект, и просмотрите содержимое Books.plist с помощью встроенного в Xcode браузера свойств. Это будет полезным инструментом позже в этой статье, когда мы начнем работать с содержимым Books.plist .

Прежде чем мы сможем начать использовать данные, хранящиеся в Books.plist , мы должны сначала заложить некоторые основы. Это включает в себя создание контроллера представления, который управляет табличным представлением, в котором будут отображаться авторы, перечисленные в Books.plist .

Вместо использования подкласса UIViewController и добавления табличного представления к представлению контроллера представления, как мы делали в предыдущей статье, мы создадим подкласс UITableViewController как я упоминал ранее в этой статье.

Начните с удаления TSPViewController.h и TSPViewController.m из вашего проекта. Создайте новый класс, выбрав New> File … в меню File . Выберите шаблон класса Objective-C в списке iOS> Шаблоны Cocoa Touch .  

Назовите новый класс TSPAuthorsViewController и сделайте его подклассом UITableViewController. Нет необходимости устанавливать флажок Также создайте файл XIB, так как мы будем использовать раскадровку для создания пользовательского интерфейса приложения.

Откройте Main.storyboard, чтобы заменить контроллер представления в раскадровке контроллером представления таблицы. Выберите контроллер представления в раскадровке, нажмите клавишу удаления и перетащите UITableViewController   Экземпляр из библиотеки объектов справа. Выберите новый контроллер представления, откройте Identity Inspector справа и установите для его класса значение TSPAuthorsViewController .

Затем выберите объект табличного представления в рабочей области или в списке объектов слева, откройте инспектор атрибутов справа и установите ячейки прототипа в 0 . Мы не будем использовать прототип клетки в этой статье.

Откройте TSPAuthorsViewController.m и проверьте содержимое файла. Поскольку TSPAuthorsViewController является подклассом UITableViewController , файл реализации заполняется стандартными реализациями требуемого и несколькими дополнительными методами протоколов UITableViewDataSource и UITableViewDelegate .

Прежде чем мы сможем отобразить данные в табличном представлении, нам нужны данные для отображения. Как я упоминал ранее, список свойств Books.plist будет служить источником данных табличного представления. Чтобы использовать данные, хранящиеся в Books.plist , нам сначала нужно загрузить их содержимое в объект, точнее массив.

Создайте свойство типа NSArray в заголовочном файле контроллера представления и NSArray ему имя « authors .

1
2
3
4
5
6
7
#import <UIKit/UIKit.h>
 
@interface TSPAuthorsViewController : UITableViewController
 
@property (strong, nonatomic) NSArray *authors;
 
@end

Метод viewDidLoad контроллера представления является хорошим местом для загрузки данных из Books.plist в свойство authors контроллера представления. Мы можем сделать это, используя NSArray класса arrayWithContentsOfFile: , arrayWithContentsOfFile:

1
self.authors = [NSArray arrayWithContentsOfFile:filePath];

Метод принимает путь к файлу, что означает, что нам нужно выяснить, каков путь к файлу Books.plist . Файл Books.plist находится в комплекте приложений, который представляет собой необычное имя для каталога, который содержит исполняемый файл приложения и ресурсы приложения — изображения, звуки и т. Д.

Чтобы получить путь к файлу Books.plist , нам сначала понадобится ссылка на основной комплект приложения, используя mainBundle , NSBundle класса NSBundle . Следующий шаг — запросить у пакета приложения путь к одному из его ресурсов, Books.plist . Мы делаем это, отправляя ему сообщение pathForResource:ofType: и передавая имя и тип (расширение) файла, для которого мы хотим получить путь. Мы сохраняем путь к файлу в экземпляре NSString как показано ниже.

1
NSString *filePath = [[NSBundle mainBundle] pathForResource:@&quot;Books&quot;

Если мы объединим эти две части, мы получим следующую реализацию viewDidLoad . Я также добавил оператор NSLog для записи содержимого свойства authors в консоль, чтобы мы могли проверить его содержимое после загрузки списка свойств.

1
2
3
4
5
6
7
— (void)viewDidLoad {
    [super viewDidLoad];
     
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@»Books» ofType:@»plist»];
    self.authors = [NSArray arrayWithContentsOfFile:filePath];
    NSLog(@»authors > %@», self.authors);
}

Если вы читали предыдущую статью этой серии, заполнение табличного представления должно быть простым. numberOfSectionsInTableView: представление будет содержать только один раздел, что делает реализацию numberOfSectionsInTableView: тривиальной.

1
2
3
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

Количество строк в единственном разделе табличного представления равно количеству авторов в массиве authors поэтому все, что нам нужно сделать, это подсчитать элементы массива.

1
2
3
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.authors count];
}

Реализация tableView:cellForRowAtIndexPath: похожа на ту, что мы видели в предыдущей статье. Основное различие заключается в том, как мы выбираем данные, отображаемые в ячейке табличного представления.

Массив авторов содержит упорядоченный список словарей, каждый из которых содержит две пары ключ-значение. Объект для ключа с именем Author является экземпляром NSString , тогда как объект для ключа Books — это массив словарей, каждый из которых представляет книгу, написанную автором. Откройте Books.plist в XCode, чтобы проверить структуру источника данных, если это не ясно.

Учитывая эту информацию, реализация tableView:cellForRowAtIndexPath: не должна быть слишком сложной.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @»Cell Identifier»;
     
    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifier];
     
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
     
    // Fetch Author
    NSDictionary *author = [self.authors objectAtIndex:[indexPath row]];
     
    // Configure Cell
    [cell.textLabel setText:[author objectForKey:@»Author»]];
     
    return cell;
}

Ключи словаря чувствительны к регистру, поэтому перепроверьте ключи, если у вас возникнут какие-либо проблемы. Создайте и запустите проект еще раз, чтобы увидеть результат.


Добавить контроллер навигации легко с помощью раскадровки. Однако, прежде чем мы добавим навигационный контроллер, важно понять, как работают навигационные контроллеры на iOS.

Как и любой другой подкласс UIViewController , контроллер навигации управляет экземпляром UIView . Представление контроллера навигации управляет несколькими подпредставлениями, включая панель навигации вверху, представление, содержащее пользовательский контент, и дополнительную панель инструментов внизу. Уникальным контроллером навигации является то, что он управляет стеком контроллеров представления.

Контроллеры навигации и просмотр иерархий контроллеров - стек навигации контроллера навигации - рисунок 11

Термин стек практически можно понимать буквально. Когда контроллер навигации инициализируется, корневой контроллер представления указывается, как мы увидим через некоторое время. Корневой контроллер представления — это контроллер представления в нижней части стека навигации .

При перемещении другого контроллера представлений в стек навигации представление корневого контроллера представлений заменяется представлением нового контроллера представлений. При работе с контроллерами навигации видимым видом всегда является вид самого верхнего контроллера вида стека навигации.

Когда контроллер представления удален или извлечен из стека навигации, представление контроллера представления ниже этого становится снова видимым. Выдвигая и вставляя контроллеры представления в и из стека навигации контроллера навигации, создается иерархия представления и, в результате, может быть создана иерархия данных. Давайте посмотрим, как все это работает на практике.

Пересмотрите раскадровку проекта ( Main.storyboard ) и выберите контроллер представления. Чтобы добавить контроллер навигации в микс, выберите « Встроить»> «Контроллер навигации» в меню « Редактор» . Несколько вещей изменились:

  • контроллер навигации является контроллером начального представления раскадровки
  • добавлена ​​новая сцена с именем Navigation Control Scene
  • добавлена ​​панель навигации в контроллер навигации и просмотра авторов
  • контроллер навигации и контроллер представления авторов связаны

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

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

Разделение между навигационным контроллером и контроллером представления авторов символизирует, что последний является корневым контроллером представления контроллера навигации.

Панель навигации в верхней части контроллера навигации и контроллера представления авторов — это то, что вы получаете бесплатно при работе с контроллерами навигации. Это экземпляр UINavigationBar и помогает перемещаться по стеку навигации.

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

Чтобы добавить заголовок на панель навигации, вставьте следующую строку в метод TSPAuthorsViewController класса TSPAuthorsViewController .

01
02
03
04
05
06
07
08
09
10
— (void)viewDidLoad {
    [super viewDidLoad];
     
    // Set Title
    self.title = @»Authors»;
     
    // Load Books
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@»Books» ofType:@»plist»];
    self.authors = [NSArray arrayWithContentsOfFile:filePath];
}

Каждый контроллер представления имеет свойство title , которое используется в различных местах, таких как панель навигации. Создайте и запустите проект, чтобы увидеть результат этого небольшого изменения.

Пришло время добавить возможность просмотра списка книг, когда пользователь нажимает на имя автора. Это означает, что нам нужно захватить выборку, имя автора, создать новый контроллер представления на основе этого выбора и поместить новый контроллер представления в стек навигации, чтобы показать его пользователю. Это звучит сложно? Это не. Позволь мне показать тебе.

Почему бы не отобразить список книг в другом табличном представлении. Создайте новый подкласс UITableViewController и назовите его TSPBooksViewController .

Загрузка списка книг проста, как мы видели ранее, но как контроллер представления книг узнает, какого автора выбрал пользователь? Есть несколько способов сообщить новому контроллеру представления о выборе пользователя, но подход, который рекомендует Apple, известен как передача по ссылке . Как это работает?

Контроллер представления книг объявляет свойство с именем author , которое может быть настроено для настройки контроллера представления книг для отображения книг выбранного автора. Откройте TSPBooksViewController.h и добавьте свойство типа NSString и назовите его author как показано ниже. Вы можете пока игнорировать ключевое слово nonatomic . Это не важно для нашей истории на данный момент.

1
2
3
4
5
6
7
#import <UIKit/UIKit.h>
 
@interface TSPBooksViewController : UITableViewController
 
@property (nonatomic) NSString *author;
 
@end

Помните из нескольких уроков назад, что преимущество свойств Objective C в том, что методы getter и setter для соответствующих переменных экземпляра генерируются для нас автоматически. Однако бывают случаи, когда необходимо или полезно реализовать собственный метод получения или установки. Это один из тех моментов.

Когда свойство TSPBooksViewController класса TSPBooksViewController установлено, источник данных табличного представления необходимо изменить. Мы сделаем это в методе _author переменной экземпляра _author . Посмотрим, как это работает.

Откройте TSPBooksViewController.m и добавьте свойство в блок @interface в верхней части файла реализации. Вы можете быть удивлены, увидев блок @interface в файле реализации класса. Это называется расширением класса и, как следует из названия, позволяет расширять интерфейс класса. Преимущество добавления расширения класса в файл реализации класса состоит в том, что свойства и переменные экземпляра, которые вы указываете в расширении класса, являются частными, что означает, что они доступны только экземплярам класса.

1
2
3
4
5
@interface MTAuthorsViewController ()
 
@property NSArray *books;
 
@end

Объявляя свойство books в расширении класса TSPBooksViewController , оно может быть доступно и изменено только экземпляром класса. Если переменная или свойство экземпляра должны быть доступны только экземплярам класса, рекомендуется объявить их как закрытые.

Метод _author переменной экземпляра _author контроллера представления вставлен ниже. Вы можете добавить его где угодно в блоке класса @implementation . Не пугайтесь реализации. Давайте начнем с вершины.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
— (void)setAuthor:(NSString *)author {
    if (_author != author) {
        _author = author;
         
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@»Books» ofType:@»plist»];
        NSArray *authors = [NSArray arrayWithContentsOfFile:filePath];
         
        int count = authors.count;
        for (int i = 0; i < count; i++) {
            NSDictionary *authorDictionary = [authors objectAtIndex:i];
            NSString *tempAuthor = [authorDictionary objectForKey:@»Author»];
             
            if ([tempAuthor isEqualToString:_author]) {
                self.books = [authorDictionary objectForKey:@»Books»];
            }
        }
    }
}

Сначала мы проверяем, отличается ли новое значение author от текущего значения _author . Подчеркивание перед author указывает, что мы непосредственно обращаемся к переменной экземпляра. Причиной этой проверки является то, что обычно нет необходимости устанавливать значение переменной экземпляра, если значение не изменилось.

Если новое значение отличается от текущего, то мы обновляем значение переменной экземпляра _author новым значением. Традиционный сеттер обычно заканчивается здесь. Однако мы реализуем пользовательский метод установки по причине, чтобы динамически устанавливать или обновлять массив books , источник данных табличного представления.

Следующие две строки должны быть знакомы. Мы загружаем список свойств из пакета приложения и сохраняем его содержимое в массиве с именем authors . Затем мы перебираем список авторов и ищем автора, который соответствует автору, сохраненному в _author .

Самый важный фрагмент этого сеттера — это сравнение между tempAuthor и _author . Если бы вы использовали двойной знак равенства ( == ) для этого сравнения, вы бы сравнивали ссылки на объекты (указатели), а не строки, которыми управляют объекты. Класс NSString определяет метод isEqualToString: который позволяет сравнивать строки вместо указателей объектов.

Для получения дополнительной информации о методах установки и получения (средства доступа) я хотел бы обратиться к документации Apple . Это стоит того, чтобы прочитать этот раздел документации.

Остальная часть класса TSPBooksViewController проста по сравнению с тем, что мы уже рассмотрели. Взгляните на реализации трех методов протокола UITableViewDataSource , показанных ниже.

1
2
3
— (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}
1
2
3
— (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.books count];
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
— (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @»Cell Identifier»;
    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifier];
     
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
     
    // Fetch Book
    NSDictionary *book = [self.books objectAtIndex:[indexPath row]];
     
    // Configure Cell
    [cell.textLabel setText:[book objectForKey:@»Title»]];
     
    return cell;
}

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

Раскадровка поможет нам в этом. Откройте Main.storyboard , перетащите другой UITableViewController экземпляр из библиотеки объектов и установите его класс TSPBooksViewController в Identity Inspector .

Выберите представление таблицы в новом контроллере представления и установите число ячеек прототипа равным 0 в инспекторе атрибутов, как мы делали для контроллера представления авторов. Чтобы поместить контроллер представления книг в навигационный стек контроллера навигации, нам нужно создать еще один переход. На этот раз, однако, нам нужно создать мануэль Segue Толчок, чтобы быть точным.

Выберите контроллер представления авторов в раскадровке, удерживайте клавишу « Control и перетащите его из контроллера представления авторов в контроллер представления книг. Выберите Push из меню, которое появляется, чтобы создать переход между двумя контроллерами представления.

Есть еще одна вещь, которую мы должны сделать, прежде чем вернуться к реализации контроллера просмотра книг. Выберите созданный нами переход, откройте инспектор атрибутов справа и установите для идентификатора перехода значение BooksViewController . Задавая имя segue, мы можем ссылаться на него в коде.

Чтобы использовать результат, нам нужно реализовать tableView:didSelectRowAtIndexPath: метод в контроллере представления авторов. Этот метод определен в протоколе UITableViewDelegate , как мы видели в предыдущей статье о табличных представлениях.

Прежде чем начать, добавьте оператор импорта в верхней части TSPAuthorsViewController.m, чтобы импортировать файл TSPBooksViewController класса TSPBooksViewController .

1
#import «TSPBooksViewController.h»

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

1
2
3
4
5
@interface TSPAuthorsViewController ()
 
@property NSString *author;
 
@end

Реализация tableView:didSelectRowAtIndexPath: коротка. Мы отменили выбор строки, которую коснулся пользователь, извлекли авторский словарь, который соответствует выбору пользователя, извлекли имя автора и выполнили переход, который соединяет контроллер представления авторов с контроллером представления книг.

01
02
03
04
05
06
07
08
09
10
— (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
     
    // Fetch Author
    NSDictionary *author = [self.authors objectAtIndex:[indexPath row]];
    self.author = [author objectForKey:@»Author»];
 
    // Perform Segue
    [self performSegueWithIdentifier:@»BooksViewController» sender:self];
}

Метод performSegueWithIdentifier:sender: принимает два аргумента: идентификатор сегмента и отправителя сообщения. Теперь должно быть понятно, почему мы дали push segue идентификатор в раскадровке?

Мы устанавливаем свойство автора контроллера представления авторов, но как контроллер представления книг узнает, какого автора выбрал пользователь? Перед выполнением перехода контроллер представления получает сообщение prepareForSegue:sender: В этом методе контроллер представления может сконфигурировать целевой контроллер представления, контроллер представления книг. Давайте реализуем prepareForSegue:sender: чтобы увидеть, как это работает.

1
2
3
4
5
6
7
8
9
— (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.destinationViewController isKindOfClass:[TSPBooksViewController class]]) {
        // Configure Books View Controller
        [(TSPBooksViewController *)segue.destinationViewController setAuthor:self.author];
         
        // Reset Author
        [self setAuthor:nil];
    }
}

Этот метод вызывается всякий раз, когда выполняется переход. Сначала мы проверяем, является ли свойство destinationViewController в TSPBooksViewController класса TSPBooksViewController . Мы используем isKindOfClass: для этого. Затем мы устанавливаем свойство author контроллера представления книг и устанавливаем для свойства автора контроллера представления авторов значение nil . Последнее является лучшей практикой.

Вам может быть интересно, когда мы инициализируем контроллер просмотра книг? Мы не создаем явно экземпляр экземпляра контроллера представления книг. Раскадровка знает, какой класс ему нужно создать, и делает это для нас.

Перед запуском приложения откройте TSPBooksViewController.m и установите для заголовка контроллера представления свойство author . Это обновит заголовок в панели навигации.

1
2
3
4
5
6
— (void)viewDidLoad {
    [super viewDidLoad];
     
    // Set Title
    self.title = self.author;
}

Постройте и запустите проект. Нажмите на имя автора в табличном представлении и посмотрите, как новый экземпляр класса TSPBooksViewController в стек навигации и отображается для пользователя. Вы заметили, что мы также получаем кнопку возврата бесплатно при использовании контроллера навигации. Название кнопки возврата взято из названия предыдущего контроллера представления.


Когда пользователь нажимает на название книги в контроллере просмотра книг, приложение должно показать обложку книги. Для этого мы не будем использовать контроллер табличного представления. Вместо этого мы используем простой ванильный подкласс UIViewController и отображаем обложку книги в экземпляре класса UIImageView . Класс UIImageView — это подкласс UIView созданный для отображения изображений.

Создайте новый подкласс UIViewController — не UITableViewController — и назовите его TSPBookCoverViewController .

Нам нужно добавить два свойства в заголовочный файл нового контроллера вида. Первое свойство имеет тип UIImage , ссылку на обложку книги, которая будет отображаться в виде изображения. Второе свойство — это ссылка на изображение, которое мы будем использовать для отображения обложки книги. Ключевое слово IBOutlet указывает, что мы установим соединение в раскадровке. Давайте сделаем это сейчас.

1
2
3
4
5
6
7
8
#import <UIKit/UIKit.h>
 
@interface TSPBookCoverViewController : UIViewController
 
@property UIImage *bookCover;
@property IBOutlet UIImageView *bookCoverView;
 
@end

Откройте раскадровку, перетащите экземпляр UIViewController из библиотеки объектов и установите для его класса значение TSPBookCoverViewController в Identity Inspector .

Перетащите экземпляр UIImageView из библиотеки объектов , сделайте так, чтобы он покрывал весь вид контроллера представления, и подключите его к bookCoverView контроллера представления.

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

Мы могли бы переопределить метод _bookCover переменной экземпляра _bookCover , но чтобы показать вам альтернативный подход, мы установили свойство изображения представления image в методе viewDidLoad контроллера представления, как показано ниже. Сначала мы проверяем, установлено ли свойство bookCover не nil а затем устанавливаем для bookCoverView image bookCoverView значение, сохраненное в свойстве bookCover .

1
2
3
4
5
6
7
— (void)viewDidLoad {
    [super viewDidLoad];
 
    if (self.bookCover) {
        [self.bookCoverView setImage:self.bookCover];
    }
}

Нам остается только показать обложку книги, когда пользователь нажмет на название книги в контроллере просмотра книг. Это означает реализацию метода делегата табличного представления tableView:didSelectRowAtIndexPath: в классе TSPBooksViewController . Не забудьте сначала импортировать файл TSPBookCoverViewController класса TSPBookCoverViewController , добавив оператор импорта вверху TSPBooksViewController.m .

1
#import «TSPBookCoverViewController.h»

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

1
2
3
4
5
6
@interface TSPBooksViewController ()
 
@property NSArray *books;
@property UIImage *bookCover;
 
@end

Реализация tableView:didSelectRowAtIndexPath: в TSPBooksViewController.m должна выглядеть знакомой. Мы выбираем книгу, которую выбрал пользователь, и создаем экземпляр UIImage используя метод imageNamed: class и передавая имя изображения. Изображение хранится в свойстве bookCover .

01
02
03
04
05
06
07
08
09
10
— (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
     
    // Fetch Book Cover
    NSDictionary *book = [self.books objectAtIndex:[indexPath row]];
    self.bookCover = [UIImage imageNamed:[book objectForKey:@»Cover»]];
     
    // Perform Segue
    [self performSegueWithIdentifier:@»BookCoverViewController» sender:self];
}

Затем мы выполняем переход с идентификатором BookCoveViewController и настраиваем контроллер представления обложки книги в prepareForSegue:sender:

1
2
3
4
5
6
7
8
9
— (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.destinationViewController isKindOfClass:[TSPBookCoverViewController class]]) {
        // Configure Book Cover View Controller
        [(TSPBookCoverViewController *)segue.destinationViewController setBookCover:self.bookCover];
         
        // Reset Book Cover
        [self setBookCover:nil];
    }
}

Класс UIImage наследуется непосредственно от одного из корневых классов Foundation, NSObject . Класс UIImage — это больше, чем контейнер для хранения данных изображений. UIImage — это мощный компонент UIKit для создания изображений из различных источников, включая необработанные данные изображений. Класс также определяет методы для рисования изображений с различными параметрами, такими как режимы наложения и значения непрозрачности.


Ранее в этой статье я объяснил, что контроллеры представления могут быть вставлены и извлечены из стека навигации. До сих пор мы только явно помещали контроллеры представления в стек навигации. Извлечение контроллера представления из стека навигации происходит, когда пользователь нажимает кнопку «Назад» на панели навигации. Это еще одна функциональность, которую мы получаем бесплатно.

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

В качестве альтернативы вы можете извлечь все контроллеры представления из стека навигации — за исключением корневого контроллера представления — отправив контроллеру навигации сообщение popToRootViewControllerAnimated:


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

В следующей статье мы рассмотрим контроллеры панели вкладок, которые также позволяют управлять коллекцией контроллеров представлений.