Как настроить внешний вид Sidebar панели NavigationView в Catalyst-приложении в SwiftUI

Для тех, кому невтерпеж ссылка на сниппет: https://gist.github.com/SoundBlaster/05362e2ee026a524f2489483edcb777d

Оказалось, что конфигурация интерфейса по умолчанию, создаваемая для вашего SwiftUI приложения, которое использует Catalyst для работы на macOS, не поддерживает внешний вид, аналогичный нативным приложениям для macOS, пусть даже они написаны на абсолютно идентичных компонентах.

Боковая левая панель NavigationView ни за что не захочет становиться прозрачной с присущим всем новым приложениям macOS эффектом затуманенного стекла или блюра. Для этого в SwiftUI существует специальный модификатор:

public func listStyle<S>(_ style: S) -> some View where S : ListStyle

Но данный модификатор не оказывает должного воздействия при сборке с целевым назначение `targetEnvironment(macCatalyst)`!

Мне очень хотелось получить такой же внешний вид, как у нового приложения «Ассистент обратной связи»:

Но получалась лишь скучная iOS-подобная панель:

Официально существует способ получить желаемый эффект в Catalyst приложении, но сделать это можно только с помощью UIKit, вручную настраивая и управляя экземпляром UISplitViewController: Apply a Translucent Background to Your Primary View Controller. Этот способ возможен, но добавляет головной боли при связывании панелей логикой навигации, выходящей за рамки SwiftUI. Хотелось получить родное для фреймворка решение.

Я подумал, как обычно бывает в таких ситуация: «Решение точно должно быть!». За годы практики получалось даже исправлять баги в UIKit, что уж говорить о том, чтобы просто включить нужное свойство.

Первым делом обратимся к иерархии компонентов интерфейса:

Ага, UISplitViewController у нас присутствует! Просто он скрыт под капотом имплементации NavigationView и нигде не торчит наружу, а про необходимость влияния на него модификатора listStyle, видимо, либо забыли, либо специально не сделали, чтобы подстегнуть создавать macOS приложения (обожаю теории заговоров!).

Дело за малым – достучаться до инстанса этого класса в иерархии и сделать то, что советует Apple в своём руководстве.

В SceneDelegate.swift в методе SceneDelegate.scene(_:willConnectTo:options:) добавляем поиск контроллера нужного класса:

let vc = type(of: self).vc(vcKind: UISplitViewController.self, window: window)

Далее меняем его свойство и включаем блюр:

if let split = vc {
    split.primaryBackgroundStyle = .sidebar

Но этого мало – UIHostingView левой панели остаётся белой и непрозрачной, скрывая от нас прекрасный размытый фон окна. Штош, ищем вьюшки и убираем фон (так можно, ведь это просто proof-of-concept):

if let sidebarController = split.viewControllers.first {
    sidebarController.view.recursiveSubviews.forEach { (view) in
    if view.backgroundColor != nil {
        view.backgroundColor = .clear
}}}

Последний штрих: скрываем для macOS панель заголовка окна:

#if targetEnvironment(macCatalyst)
if let titlebar = windowScene.titlebar {
    titlebar.titleVisibility = .hidden
    titlebar.toolbar = nil
}
#endif

Готово!

UPD 31.05.2021

Еще одно интересное решение этой проблемы представлено в репозитории https://github.com/steventroughtonsmith/CatalystSidebarToolbar

Опубликовано

в

от

Метки:

Комментарии

Добавить комментарий