Различные виды панели навигации для разных вкладок в TabView в SwiftUI

У меня есть TabView, на котором есть 3 вкладки («Обязательства», «Входы/выходы» и «Активы»). На каждой вкладке есть NavigationView. Я хочу, чтобы для каждого из них был разный внешний вид навигационной панели (красная тема для пассивов, белая для входа/выхода и зеленая тема для активов).

Я могу без труда установить цвета фона навигационных панелей, используя что-то вроде этого:

.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.toolbarBackground(Color.liabilitiesnav, for: .navigationBar)

Это позволяет мне установить только цвет фона, но мне нужно иметь возможность изменять цвета других элементов на панели навигации. Кнопками, которые я добавляю на панель инструментов, я могу управлять, явно устанавливая их цвета, так что это не проблема. Но заголовком навигации, текстом и значком кнопки «Назад» можно управлять только с помощью глобальной функциональности UINavigationBar.appearance(). Но мне не нужен глобальный внешний вид, я хочу настроить разные вкладки с разным внешним видом. Это действительно важно, потому что мой AccentColor — темно-зеленый, и хотя этот зеленый цвет хорошо смотрится на кнопке «Назад» и элементах панели инструментов на вкладке «Активы»… он ужасно зеленый на красном на вкладке «Обязательства». Вот почему мне нужно иметь возможность управлять ими отдельно.

Я пробовал использовать механику .onAppear { }, чтобы попытаться изменить глобальный внешний вид, чтобы он соответствовал текущей вкладке всякий раз, когда эта вкладка появляется. Например, на вкладке «Обязательства» у меня есть:

NavigationView {
    List {
        // stuff
    }
    .onAppear {
        // tried it here...
        NavHelper.useRedAppearance()
    }
} 
.onAppear {
    // and tried it here as well
    NavHelper.useRedAppearance()
}

Однако, похоже, это рассинхронизируется. Все начнется правильно (Обязательства = красный и Активы = зеленый), но когда я щелкаю вперед и назад между вкладками «Обязательства» и «Активы», обновления, кажется, не синхронизируются, и иногда «Обязательства» отображаются зеленым, а иногда «Активы» отображаются красным. . Я добавил несколько операторов печати в код onAppear и увидел, что useRedAppearance() вызывается, когда я нажимаю на вкладку «Обязательства», а useGreenAppearance() вызывается, когда я нажимаю на вкладку «Активы»... но цвета не обязательно обновляются. каждый раз... и таким образом рассинхронизировались.

Вот частичная вставка NavHelper на случай, если я делаю что-то не так:

class NavHelper {   

    static func useRedAppearance() {
        let textcolor = UIColor.moneyred
        let backgroundcolor = UIColor.liabilitiesnav
        
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        appearance.backgroundColor = backgroundcolor
        appearance.titleTextAttributes = [.foregroundColor: textcolor]
        appearance.largeTitleTextAttributes = [.foregroundColor: textcolor]
        
        let buttonAppearance = UIBarButtonItemAppearance()
        buttonAppearance.normal.titleTextAttributes = [.foregroundColor: textcolor]
        
        let image = UIImage(systemName: "chevron.backward")!.withTintColor(textcolor, renderingMode: .alwaysOriginal)
        appearance.setBackIndicatorImage(image, transitionMaskImage: image)
        
        appearance.buttonAppearance = buttonAppearance
        appearance.backButtonAppearance = buttonAppearance
        
        UINavigationBar.appearance().standardAppearance = appearance
        UINavigationBar.appearance().scrollEdgeAppearance = appearance
        UINavigationBar.appearance().compactAppearance = appearance
        UINavigationBar.appearance().compactScrollEdgeAppearance = appearance
        
    }

}

Как я могу либо (а) надежно переключать глобальный внешний вид вперед и назад, не теряя синхронизации, либо (б) индивидуально настраивать представления на разных вкладках, чтобы у них была только фиксированная цветовая тема?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Цвета не синхронизируются, поскольку API UIAppearance не может изменить внешний вид существующих представлений. Иногда панель навигации перемещается в окно до вызова useRedAppearance, поэтому на нее это не влияет.

Если вы просто хотите контролировать цвет кнопки «Назад», вы можете использовать tint:

NavigationStack {
    SomeView()
        // 'tint' on the NavigationStack also affects tints of other views in the navigation stack
        // so we apply another 'tint' here, to change the tint back
        .tint(Color.accent)
}
// this sets the back button color to red
.tint(.red)

Если вы также хотите изменить цвет заголовка, поместите цветной Text в качестве элемента .principal панели инструментов.

.toolbar {
    ToolbarItem(placement: .principal) {
        Text("Liabilities")
            .foregroundStyle(.yellow)
    }
}

Альтернативно, вы можете использовать UIViewControllerRepresentable, чтобы получить UINavigationController и установить для него цвета.

struct NavigationAppearance: UIViewControllerRepresentable {
    var textColor: UIColor
    var backgroundColor: UIColor
    
    class VC: UIViewController {
        var textColor: UIColor = .clear
        var backgroundColor: UIColor = .clear
        
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            updateBarAppearance()
        }
        
        func updateBarAppearance() {
            let appearance = UINavigationBarAppearance()
            appearance.configureWithOpaqueBackground()
            appearance.backgroundColor = backgroundColor
            appearance.titleTextAttributes = [.foregroundColor: textColor]
            appearance.largeTitleTextAttributes = [.foregroundColor: textColor]
            
            let buttonAppearance = UIBarButtonItemAppearance()
            buttonAppearance.normal.titleTextAttributes = [.foregroundColor: textColor]
            
            let image = UIImage(systemName: "chevron.backward")!.withTintColor(textColor, renderingMode: .alwaysOriginal)
            appearance.setBackIndicatorImage(image, transitionMaskImage: image)
            
            appearance.buttonAppearance = buttonAppearance
            appearance.backButtonAppearance = buttonAppearance
            
            navigationController?.navigationBar.standardAppearance = appearance
            navigationController?.navigationBar.scrollEdgeAppearance = appearance
            navigationController?.navigationBar.compactAppearance = appearance
            navigationController?.navigationBar.compactScrollEdgeAppearance = appearance
            
        }
    }
    
    func makeUIViewController(context: Context) -> VC {
        VC()
    }
    
    func updateUIViewController(_ uiViewController: VC, context: Context) {
        uiViewController.textColor = textColor
        uiViewController.backgroundColor = backgroundColor
    }
}

Все, что вам нужно сделать, это поместить это где-нибудь в иерархию представлений, например. background

NavigationStack {
    SomeView()
        .navigationTitle("Assets")
        .navigationBarTitleDisplayMode(.inline)
        .background {
            NavigationAppearance(textColor: .systemRed, backgroundColor: .systemGreen)
        }
}

Большое спасибо! Переопределение оттенка на правильный цвет (а затем обратно для внутренних компонентов) было очень полезным, и элемент панели инструментов «Основное положение» помог мне легко решить и эту проблему. Спасибо!

Kenny Wyland 28.08.2024 21:01

Другие вопросы по теме