iOS
Для начала откройте
Main.storyboard
. Перетащите новый объект
View Cont-
roller
на холст из панели
Library
(Библиотека), как мы делали это, добавляя
в приложение другие экраны. В настоящее время этот экран изолирован от
остальной части приложения, поэтому подключим его через переход, чтобы
его можно было открыть позже.
В этом редактор раскадровки бесподобен! Если щелкнуть на ячейке таб-
личного представления в сцене каталога – той, что мы создали в главе 17, –
в структуре документа в редакторе раскадровки, вы сможете перетащить ее,
удерживая нажатой клавишу
Ctrl
, в нужную точку и подключить переход
непо
-
средственно
к самой ячейке.
В появившемся диалоге выберите
Show
(Показать) в разделе
Action Segues
(Действие перехода). Если теперь собрать и запустить приложение, вы увиди
-
те, что касание книги в каталоге помещает в стек представлений новый конт-
Детализация информации о книгах
315
роллер – пустой в настоящее время. Однако уже сейчас наблюдаются некото
-
рые странности в отображении интерфейса, которые нужно исправить. Если
вернуться в представление каталога, можно заметить, что ячейка таблично
-
го представления продолжает иметь вид выбранной. Кроме того, наше новое
представление вообще ничего не содержит – это просто белый экран. Испра
-
вим это!
Добавим деталей для вывода деталей
Для исправления обоих недостатков нужно создать свой контроллер представ
-
ления для новой сцены. Добавьте в проект новый файл с определением клас
-
са
DetailViewController
. Он должен наследовать
UIViewController
, как показано
ниже:
import UIKit
class DetailViewController: UIViewController {
}
Предыдущий опыт создания списка, а также опыт вывода детальной ин
-
формации в Android подсказывают, что мы должны добавить несколько пред
-
ставлений в наш контроллер. Все они будут экземплярами меток, поэтому про
-
должим и добавим несколько выходов; мы подключим их позже в редакторе
раскадровки.
После добавления выходов контроллер представления должен выглядеть
так:
import UIKit
class DetailViewController: UIViewController {
@IBOutlet var titleLabel: UILabel!
@IBOutlet var authorsLabel: UILabel!
@IBOutlet var isbnLabel: UILabel!
@IBOutlet var pageCountLabel: UILabel!
@IBOutlet var fictionLabel: UILabel!
}
Но как мы будем заполнять эти свойства?
Мы знаем, что исходная информация поступит в форме объекта
Book
, поэто
-
му создадим метод
populate(from:)
, который принимает аргумент
Book
и запол
-
няет метки значениями его свойств. Этот объект будет передаваться контрол
-
лером представления каталога во время перехода. Вот как должна выглядеть
окончательная версия класса с этим методом:
import UIKit
class DetailViewController: UIViewController {
@IBOutlet var titleLabel: UILabel!
@IBOutlet var authorsLabel: UILabel!
@IBOutlet var isbnLabel: UILabel!
@IBOutlet var pageCountLabel: UILabel!
@IBOutlet var fictionLabel: UILabel!
func populate(from book: Book) {
316
Сохранность данных
titleLabel.text = book.title
// перечислить авторов в строке через запятую
authorsLabel.text = book.authors.joined(separator: ", ")
isbnLabel.text = book.isbn
pageCountLabel.text = book.pageCount.description
// По логическому флагу определить категорию книги
fictionLabel.text = book.fiction ? "Fiction" : "Nonfiction"
}
}
Теперь вернемся к
CatalogViewController
и подключим это соединение. Как вы
помните, наш переход запускается автоматически, потому что мы подключи
-
ли его к самой ячейке табличного представления. Подготовка к переходу про
-
исходит в контроллере представления, вызвавшем переход. Существует даже
специальный метод, который можно переопределить, чтобы добавить свои
подготовительные операции:
prepare(for:sender:)
. Он принимает в первом
параметре объект
UISegue
, содержащий целевой контроллер представления,
в данном случае – наш
DetailViewController
.
Но, прежде чем использовать этот метод, нужно получить экземпляр кни
-
ги, соответствующий ячейке. Для этого добавим в
ListDataSource
новый метод
book(for:)
, который будет получать индекс из табличного представления и воз
-
вращать книгу с этим индексом из источника данных:
func book(for indexPath: IndexPath) > Book {
return data[indexPath.row]
}
Теперь переопределим метод
prepare(for:sender:)
в
CatalogViewController
,
в котором заполним целевое представление перед переходом:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let detailViewController = segue.destination as? DetailViewController,
let indexPath = tableView.indexPathForSelectedRow {
detailViewController.populate(from: dataSource.book(for: indexPath))
}
}
Метод получился коротким, но для вас очень важно понять, что в нем происхо
-
дит. Сначала проверяется тип целевого контроллера, это должен быть контрол
-
лер
DetailViewController
. Затем проверяется, имеется ли в табличном представ
-
лении выбранный элемент. Обе эти проверки важны, потому что данный метод
вызывается для каждого перехода, запускаемого этим контроллером представ
-
ления. Если оба условия выполняются, для экземпляра
DetailViewController
вы
-
зывается метод, который мы создали ранее, чтобы получить экземпляр книги.
Теперь, прежде чем оставить этот файл, вернемся в редактор раскадровки
и добавим еще одно исправление: сбросим выделение элемента в табличном
представлении. Это необходимо, потому что в качестве базового класса мы
использовали стандартный
UIViewController
вместо
UITableViewController
. Нам
нужно, чтобы выделение сбрасывалось при каждом появлении представле
-
ния, что обеспечит красивую анимацию затухания и даст нашим пользовате
-
Детализация информации о книгах
317
лям возможность заметить, какой элемент они выбрали. Используем для этого
метод
viewDidAppear(_:)
, являющийся частью жизненного цикла контроллера
представления.
Вот как должна выглядеть окончательная версия нашего класса
CatalogView
Controller
:
import UIKit
class CatalogViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
lazy var dataSource: ListDataSource = {
return ListDataSource()
}()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = dataSource
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let indexPath = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: indexPath, animated: true)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let detailViewController = segue.destination as? DetailViewController,
let indexPath = tableView.indexPathForSelectedRow {
detailViewController.populate(from: dataSource.book(for: indexPath))
}
}
}
И последнее, что нужно сделать с новым представлением, – добавить в сцену
метки для новых выходов в
DetailViewController
. Вы можете расположить их,
как вам заблагорассудится. Проявите творческий подход! При желании можете
заглянуть в пример проекта, доступный в нашем репозитории GitHub. В нем
мы использовали представления стека (из панели
Library
(Библиотека)), что
-
бы получить возможность автоматически сжимать и распахивать их в зависи
-
мости от размера экрана. Однако вы можете пойти по более простому пути:
перетащить в сцену метки и добавить некоторые ограничения Auto Layout. Вы
должны получить нечто, похожее на рис. 19.1.
Теперь переключим эту сцену на использование нашего класса
DetailViewCon
troller
. Для этого откройте инспектор идентичности справа в окне редактора
и замените
UIViewController
на
DetailViewController
. Далее, удерживая клавишу
Ctrl
, перетащите объект контроллера представления из схемы документа на
каждую метку и для каждой выберите соответствующий выход из числа тех,
что мы определили ранее.
Теперь соберите и запустите проект, перейдите в каталог и коснитесь любо
-
го элемента. В ответ должен открыться новый экран с подробной информаци
-
ей из этого элемента. Здорово!
Do'stlaringiz bilan baham: |