Создание сложных интерфейсов: iOS-разработка на SwiftUI 0:05
Лекция №10 курса Stanford CS193p (весна 2025 года) полностью посвящена практической разработке интерфейсов на SwiftUI. Лектор демонстрирует эволюцию приложения для игры в Mastermind, переходя от простых списков к сложной навигации, работе с данными как классами и кастомизации UI-элементов.
🧩 Идентификация и протоколы Hashable/Identifiable 0:35
Для корректной работы ForEach в SwiftUI каждый элемент коллекции должен быть уникальным, стабильным и соответствовать протоколу Hashable.
- Проблема уникальности: При использовании
ForEachс элементами данных необходимо задать идентификатор. Использованиеid: \.selfтребует, чтобы тип данных былHashable. - Синтез Hashable: Swift может автоматически синтезировать
Hashableдля структур, если все их поля также являютсяHashable. ЕслиKind(enum) или другие свойства не соответствуют этому требованию, их нужно обновить вручную. - Использование pegs: Лектор отмечает, что использование
pegs(колышков) в качестве уникального идентификатора часто является более семантически верным и простым решением, чем перегрузка структуры протоколами. - Identifiable: Если модель реализует протокол
Identifiable, можно опустить явное указаниеid:, так как SwiftUI будет использовать свойствоidавтоматически. Однако лектор предостерегает от чрезмерного использованияIdentifiableдля простых структур, если их идентификация зависит от контекста (например, поиск по цвету).
📜 Архитектура списков (List) 14:34
Компонент List является базовым инструментом для создания сложных интерфейсов в iOS, объединяя функциональность VStack, ScrollView и ForEach.
- Динамические списки:
Listможет динамически отображать коллекции данных, предоставляя гибкие возможности для кастомизации каждой строки. - Морфология: Для работы с множественным числом (например, "1 попытка" против "2 попыток") в SwiftUI встроен механизм морфологии, который корректно склоняет слова в зависимости от языка, избегая примитивных тернарных операторов.
- Кастомизация: С помощью модификаторов (например,
.listRowSeparator,.listBackground) можно менять внешний вид строк, скрывать разделители и добавлять фон. - Секции:
Listподдерживает разбиение на логические разделы (Section), что полезно для группировки настроек или различных типов данных.
🗺 Навигация: Stack и Destination 32:55
Для перехода между экранами SwiftUI использует систему навигации, управляемую NavigationStack.
- NavigationStack: Создает среду, в которой работают
NavigationLink. Этот компонент автоматически управляет кнопкой «Назад» и заголовками. - Передача данных: Для обмена данными между списком и детальным представлением используется
@Binding. Это позволяет изменениям в игре мгновенно отражаться в списке. - Toolbar: Модификатор
.toolbarпозволяет добавлять кнопки (например,EditButtonдля удаления/сортировки строк) в панель навигации. Использование семантических позиций (например,.primaryAction) вместо жесткого задания сторон позволяет SwiftUI лучше адаптировать интерфейс под разные устройства.
🏛 Модели данных: Struct vs Class 51:33
Лектор сравнивает подход работы с данными через структуры (передача по значению/Binding) и классы (передача по ссылке).
- @Observable: При использовании классов в качестве модели данных необходимо помечать их как
@Observable. Это позволяет SwiftUI «видеть» изменения, происходящие внутри объекта в куче (heap). - Эффективная навигация: Для больших списков использование
.navigationDestination(for:...)предпочтительнее, чем созданиеNavigationLinkнапрямую с представлением, так как это предотвращает ненужное создание объектов, которые пока не отображаются на экране. - Hashable для навигации: Чтобы использовать типизированную навигацию с
navigationDestination, модель данных должна соответствовать протоколамIdentifiableиHashable.