О времена… они .concact(.haveBeen) меняются. В прошлые годы я ждал, когда ближе к концу недели WWDC выйдет видео «What’s New in UIKit», внимательно изучал новые документы, чтобы понять, что добавили, а потом на ощупь разбирался в Xcode, пытаясь запустить первые примеры новых возможностей.
А теперь? Ну, теперь я просто… примерно сделал вот так:
В считанные минуты у меня уже были рабочие примеры кода со всеми изменениями, объяснения того, что они собой представляют, и документация. Я молился о таких днях, чувак.
Для таких вещей теперь даже есть отдельный skill. Хочется верить, что Cupertino & Friends™️ сделали его специально, чтобы подкинуть мне косточку для моего ежегодного поста про UIKit.
На сегодняшний день использование UIKit практически ничего не стоит. Я бы сказал, что сейчас самое подходящее время (да, действительно!), чтобы попробовать наш старый добрый UI-фреймворк в вашем приложении. Apple предоставила доступ к навыкам, позволяющим сделать это правильно, эффективно и быстро. Итак, давайте посмотрим, что нового.
Панель навигации и кнопки панели
Панель навигации и её компоненты получили немало внимания, и, честно говоря, именно здесь мы найдём большинство изменений. Они адаптируются к самым разным размерам и ситуациям… хм, интересно, почему?!
Минимизация панели навигации
Теперь каждый контроллер представления может влиять на то, будет ли его панель навигации уменьшаться при прокрутке. Это отлично подходит для экранов с большим количеством контента, где важна каждая вертикальная точка, в то время как ваши настройки или экраны с инструментами могут удерживать панель на месте. Также существует сопутствующий API безопасной области, который определяет, будет ли ваш контент сдвигаться вверх в пространство, оставшееся после минимизации панели.
navigationItem.barMinimizeBehavior = .onScrollDown navigationItem.barMinimizationSafeAreaAdjustment = .enabled
Приоритет видимости кнопок в панели
Теперь элементы bar button можно ранжировать, чтобы UIKit понимал, какие действия нужно сохранять на экране дольше всего, когда места становится мало. Представьте iPad, Stage Manager, тулбары, окна с изменяемым размером, складные устройства — везде, где доступная ширина панели постоянно меняется. Самые важные элементы, которым вы задали приоритет, остаются на месте, а остальные уходят.
saveItem.visibilityPriority = .high shareItem.visibilityPriority = .standard printItem.visibilityPriority = UIBarButtonItemVisibilityPriority(lowerThan: .standard)
Удалён отступ кнопки на панели инструментов
Делает именно то, что указано в названии — удаляет стандартный внешний отступ у элемента UIBarButtonItem. Думаю, это будет удобно, если ваш пользовательский вид кнопки на панели инструментов уже имеет свой собственный отступ, или вам просто нужно, чтобы всё выравнивалось немного плотнее. Просто следите за областями касания, действует золотое правило 44 точек:
let colorItem = UIBarButtonItem(customView: colorSwatch) colorItem.isPaddingRemoved = true
Сцены, окна и ориентация
Apple продолжает двигаться к миру с несколькими окнами, изменяемыми размерами, сценами повсюду и… да ладно, забудьте. Всё из-за грядущего складного устройства, вот почему. К слову, Device Hub позволяет быстро всё это протестировать.
Подтверждение закрытия сцены
Бывало, закрываешь окно и тут же жалеешь об этом? Теперь приложение может показать системное подтверждение перед фактическим закрытием сцены — примерно так, как сегодня делают многие веб-приложения. Это рассчитано на многооконные document-based приложения, редакторы и workflow-приложения — в общем, на любые сценарии, где резкое закрытие окна может выбросить несохранённую работу или прервать процесс на полпути.
windowScene.closureConfirmation = UISceneClosureConfirmation(
title: "Unsaved Changes",
message: "Save before closing this window?",
actions: [
UIAlertAction(title: "Save and Close", style: .default) { _ in saveDocument() },
UIAlertAction(title: "Discard Changes", style: .destructive) { _ in discardChanges() }
]
)
Дополнительные аксессуары для сцен внешнего дисплея
Теперь контроллер представления может регистрировать дополнительную неинтерактивную сцену, созданную для внешнего дисплея. Судя по документации, это было бы здорово для вспомогательных экранов презентаций, панелей мониторинга, справочных представлений или любого дополнительного вывода, который вы хотели бы отображать на подключенном дисплее, где сенсорный ввод не всегда требуется:
let configuration = UISceneConfiguration(name: "Presenter Display") let accessory = UISceneAccessory.externalNonInteractive(sceneConfiguration: configuration) let registration = registerSceneAccessory(accessory) registration.isEnabled = registration.isAvailable
Поддерживаемые ориентации для каждой сцены
Теперь каждая оконной сцены можно объявлять собственный набор поддерживаемых ориентаций. Поэтому в многооконном приложении сцена с видео может оставаться зафиксированной в ландшафте, а сцена для просмотра или редактирования — спокойно поворачиваться как угодно. Такая гибкость очень кстати: у меня уже несколько раз были ситуации, когда универсальные правила ориентации для всего приложения просто не подходили.
func supportedInterfaceOrientations(
for windowScene: UIWindowScene
) -> UIInterfaceOrientationMask {
.landscape
}
Таб-бары и сайдбары
Связка таб-бара и сайдбара — причём последний отказался от дизайна из iOS 26, который мне так нравился — получила несколько полезных настроек для управления тем, что именно показывать и чему отдавать главный акцент.
Выделение важной вкладки
Есть вкладка, которая важнее остальных? Теперь UITabBarController может визуально выделить одну вкладку — отличный вариант для ключевого действия вроде написания сообщения, поиска, корзины, создания чего-либо или записи.
tabBarController.prominentTabIdentifier = "compose"
tabBarController.setProminentTabIdentifier("inbox", animated: true)
Предпочтительное размещение
Когда на экране может остаться только один вариант, эта настройка позволяет tab bar controller указать, что он предпочитает показывать: сайдбар или таб-бар. Это отлично подходит для приложений, которые по своей природе ориентированы на сайдбар, но всё же должны аккуратно сворачиваться в таб-бар на более компактных устройствах или… ну вы уже понимаете. Полагаю, речь о складных устройствах.
tabBarController.mode = .tabSidebar tabBarController.sidebar.preferredPlacement = .sidebar
Пакетное обновление панели вкладок
Изменяете сразу множество параметров вкладок — значки, изображения, порядок, состояние видимости — и хотите избежать мерцания? Пакетное обновление. UIKit объединяет все эти изменения в один проход обновления, вместо того чтобы выполнять отдельный проход компоновки для каждого.
tabBarController.performBatchUpdates {
tabBarController.tabs[0].badgeValue = "3"
tabBarController.tabs[1].image = UIImage(systemName: "star.fill")
tabBarController.setProminentTabIdentifier("favorites", animated: false)
}
Отслеживание доступности боковой панели
Простая проверка наличия боковой панели в вашем интерфейсе в данный момент. Это удобно для корректировки вспомогательного пользовательского интерфейса, когда боковая панель появляется или исчезает из-за изменения класса, поворота или размера окна.
func tabBarController(
_ tabBarController: UITabBarController,
sidebarAvailabilityDidChange sidebar: UITabBarController.Sidebar
) {
tabBarController.navigationItem.leftBarButtonItem = sidebar.isAvailable ? nil : menuButton
}
Бонус
И, как всегда, есть еще и обычный набор мелочей. Вот несколько мелочей, которые не совсем вписались в вышеперечисленное, но все же заслуживают внимания.
Видимость изображений в меню
Теперь элементы меню могут явно указывать, показывать изображения, скрывать их или оставить решение системе. Используйте это, когда иконка сама несёт важную информацию — например, в случае цветовых образцов. А когда иконки только добавляют визуальный шум в меню с большим количеством текста, их можно просто скрыть.
let red = UIAction(
title: "Red",
image: UIImage(systemName: "circle.fill"),
preferredImageVisibility: .visible
) { _ in applyColor(.systemRed) }
let reset = UIAction(
title: "Reset Formatting",
image: UIImage(systemName: "textformat"),
preferredImageVisibility: .hidden
) { _ in resetFormatting() }
Подзаголовок на экране запуска документа
Ну а почему бы и нет? Подзаголовки!
let launchOptions = UIDocumentViewController.LaunchOptions() launchOptions.subtitle = "Create and edit presentations" let documentViewController = UIDocumentViewController(launchOptions: launchOptions)
Заключение
Ещё несколько лет назад я задавался вопросом, не собирается ли Apple вообще отправить UIKit в утиль. Это, конечно, было бы радикально, но такие мысли у меня были. Сейчас я уже так не думаю. SwiftUI с каждым годом становится лучше, и да — именно он будет на первых ролях. Но UIKit остаётся надёжным фреймворком и, судя по всему, продолжит быть важной частью стратегии Apple в будущем.
В этом году изменилось не так уж много, и почти ничего из добавленного не выглядит особенно эффектно. Но в каком-то смысле это справедливо и для iOS 27 в целом. Этот год посвящён тому, чтобы наконец довести Siri до ума, и, на мой взгляд, это правильный шаг.
До следующего раза ✌️

