Стенфордский курс CS193p продолжает погружение в экосистему SwiftUI: одиннадцатая лекция Пола Хэгарти (Paul Hegarty) целиком посвящена адаптации приложений для iPad и Mac. Основное внимание уделяется переходу от простых стеков навигации к сложным разделенным интерфейсам и созданию мощных инструментов редактирования данных.
📱 От iPhone к iPad: внедрение NavigationSplitView 1:06
Перенос приложения с iPhone на iPad часто сопровождается проблемой, которую Пол Хэгарти называет «гигантским режимом» (gigantic mode). Это ситуация, когда интерфейс просто растягивается на весь огромный экран, не используя доступное пространство эффективно. Вместо стандартного NavigationStack, который работает как колода карт (новое окно поверх старого), лектор предлагает использовать NavigationSplitView.
NavigationSplitView разделяет экран на несколько функциональных зон (панелей):
- Sidebar (левая панель): используется для выбора категорий или объектов.
- Content (средняя панель): промежуточный уровень фильтрации (опционально).
- Detail (правая панель): основная рабочая область, где отображается выбранный контент.
Для гармоничного распределения пространства между панелями используется модификатор .navigationSplitViewStyle(.balanced). Также Пол Хэгарти демонстрирует управление видимостью колонок через параметр columnVisibility. Интересный прием лектора — использование .constant(.all), что позволяет приложению всегда запускаться с открытым списком игр, при этом сохраняя пользователю возможность скрыть его вручную.
⚙️ Размерные классы и адаптивность 16:10
Важным открытием для студентов становится то, что SwiftUI принимает решение о смене интерфейса (стек или разделенный вид) не на основе типа устройства, а на основе «размерных классов» (size classes).
Основные тезисы Пола Хэгарти о размерных классах:
- Compact (компактный): характерен для iPhone в портретном режиме. В этом случае
NavigationSplitViewавтоматически превращается вNavigationStack. - Regular (регулярный): характерен для iPad и больших iPhone (например, моделей Plus/Max) в ландшафтной ориентации.
- Environment: разработчик может отслеживать текущий класс через переменную окружения
@Environment(\.horizontalSizeClass)и программно менять логику интерфейса.
🎯 Управление состоянием выбора в списках 19:42
Стандартный List в SwiftUI может управлять «выбором» (selection) неявно, но для профессиональной разработки требуется полный контроль над состоянием. Пол Хэгарти вводит переменную @State private var selection: CodeBreaker?, которая связывается со списком через параметр selection:.
Связь работает через типы данных: если значение в NavigationLink(value:) совпадает по типу с переменной выбора, SwiftUI автоматически устанавливает связь. Это позволяет реализовать функцию автоматического выбора первой или случайной игры при запуске приложения. Лектор подчеркивает важность использования if let selection в области Detail: если игра не выбрана, приложение должно показывать заглушку, например, текст «Choose a game!».
🖥️ Особенности разработки для Mac 27:49
Одним из самых быстрых способов выпустить приложение для macOS Пол Хэгарти называет режим «Mac (Designed for iPad)». Приложение запускается в среде macOS, сохраняя логику iPad, но приобретая черты настольной ОС: меню, изменяемые размеры окон и поддержку мыши.
Ключевые отличия взаимодействия на Mac по сравнению с iOS:
- Жесты против мыши: на Mac нет «свайпа для удаления», так как мышь не поддерживает такие жесты. Вместо этого используется контекстное меню (правая кнопка мыши).
- Редактирование списков: если на iPad для перемещения элементов списка часто требуется вход в специальный Edit Mode, то на Mac элементы можно перетаскивать мышью сразу.
Для реализации удаления Пол Хэгарти добавляет модификатор .contextMenu. Внутри него размещается кнопка с ролью .destructive, которая в iOS подсвечивается красным цветом, предупреждая об опасности действия. Чтобы интерфейс оставался чистым после удаления активной игры, лектор внедряет метод .onChange(of: games), который сбрасывает selection в nil, если выбранный объект исчез из массива.
🛠️ Формы и редактирование данных через @Bindable 46:59
Заключительная часть лекции посвящена созданию GameEditor — интерфейса для настройки параметров игры. Для сбора информации от пользователя Пол Хэгарти рекомендует использовать компонент Form. Он автоматически применяет стилизацию, характерную для системных настроек iOS: группировку элементов в белые блоки, отступы и автоматическую прокрутку при появлении клавиатуры.
В процессе работы лектор объясняет концепцию @Bindable. В Swift 5.10+ и SwiftUI 2024–2025 годов для создания двусторонней связи (Binding) с полями класса, помеченного как @Observable, необходимо явно указывать обертку @Bindable внутри тела View. Без этого попытка использовать знак $ для связи с текстовым полем приведет к ошибке компиляции.
Для выбора цветов лектор использует встроенный компонент ColorPicker. Он поддерживает выбор из спектра, ввод RGB-значений и стандартную палитру. Процесс редактирования удобно отслеживать в консоли через модификатор .onChange(of:) прямо в окне предварительного просмотра (#Preview).