Stanford CS193p: как адаптировать SwiftUI-приложения для iPad и Mac

Stanford Online 3,4 тыс. 1 ч 1 мин 3 мин 03.12.2025
Главное

Стенфордский курс CS193p продолжает погружение в экосистему SwiftUI: одиннадцатая лекция Пола Хэгарти (Paul Hegarty) целиком посвящена адаптации приложений для iPad и Mac. Основное внимание уделяется переходу от простых стеков навигации к сложным разделенным интерфейсам и созданию мощных инструментов редактирования данных.

📱 От iPhone к iPad: внедрение NavigationSplitView 1:06

Перенос приложения с iPhone на iPad часто сопровождается проблемой, которую Пол Хэгарти называет «гигантским режимом» (gigantic mode). Это ситуация, когда интерфейс просто растягивается на весь огромный экран, не используя доступное пространство эффективно. Вместо стандартного NavigationStack, который работает как колода карт (новое окно поверх старого), лектор предлагает использовать NavigationSplitView.

NavigationSplitView разделяет экран на несколько функциональных зон (панелей):

Для гармоничного распределения пространства между панелями используется модификатор .navigationSplitViewStyle(.balanced). Также Пол Хэгарти демонстрирует управление видимостью колонок через параметр columnVisibility. Интересный прием лектора — использование .constant(.all), что позволяет приложению всегда запускаться с открытым списком игр, при этом сохраняя пользователю возможность скрыть его вручную.


⚙️ Размерные классы и адаптивность 16:10

Важным открытием для студентов становится то, что SwiftUI принимает решение о смене интерфейса (стек или разделенный вид) не на основе типа устройства, а на основе «размерных классов» (size classes).

Основные тезисы Пола Хэгарти о размерных классах:

  1. Compact (компактный): характерен для iPhone в портретном режиме. В этом случае NavigationSplitView автоматически превращается в NavigationStack.
  2. Regular (регулярный): характерен для iPad и больших iPhone (например, моделей Plus/Max) в ландшафтной ориентации.
  3. 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:

Для реализации удаления Пол Хэгарти добавляет модификатор .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).

💬 Цитаты

«Многие студенты делают приложение под iPhone и говорят: «О, я могу поддерживать iPad, просто включу его». И в итоге получают «гигантский режим».»

Пол Хэгарти 2:05

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

Пол Хэгарти 15:52
👥 Спикер
🔗 Упомянутые сайты и проекты
📖 Термины
NavigationSplitView
Контейнер в SwiftUI, который разделяет экран на две или три колонки (панели) для навигации.
Size Class
Система классификации размеров экрана (Compact или Regular) для адаптации интерфейса под разные устройства.
@Bindable
Обертка свойства, позволяющая создавать двустороннюю связь с данными внутри объектов, использующих современный макрос @Observable.
Context Menu
Меню, которое появляется при нажатии правой кнопкой мыши (на Mac) или при долгом нажатии (на iOS).
📊 Цифры
🗓 Хронология
  1. весна 2025 Проведение текущего курса Stanford CS193p.
⚖️ Другая сторона
Образование Пол Хэгарти SwiftUI NavigationSplitView iPad macOS