У меня есть 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
}
}
Как я могу либо (а) надежно переключать глобальный внешний вид вперед и назад, не теряя синхронизации, либо (б) индивидуально настраивать представления на разных вкладках, чтобы у них была только фиксированная цветовая тема?
Цвета не синхронизируются, поскольку 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)
}
}
Большое спасибо! Переопределение оттенка на правильный цвет (а затем обратно для внутренних компонентов) было очень полезным, и элемент панели инструментов «Основное положение» помог мне легко решить и эту проблему. Спасибо!