Мне наконец удалось выяснить, как изменить заголовок моего NavigationView на основе текущей вкладки в TabbedView с помощью onAppear(), но я почти уверен, что это не тот способ:
struct SomeTabbedView : View {
@State private var selection = 0
@State private var title = "First"
var body: some View {
NavigationView {
TabbedView(selection: $selection) {
ContentView()
.tabItemLabel(
Text("First")
).tag(0).onAppear{
self.title = "First"
}
OtherView()
.tabItemLabel(
Text("Second")
).tag(1).onAppear{
self.title = "Second"
}
}.navigationBarTitle(Text(title))
}
}
}
Короче говоря, это довольно сильно нарушает принцип DRY. Если мы решим изменить первое представление, например, строку «First», текущая реализация потребует повторить это в трех разных местах.
Я думал об инициализации массива словарей и переборе пар View: Label, но я не только не уверен, что это правильный путь, я не уверен, как продолжить его реализацию. Также можно просто иметь массив с метками, но это создает еще одно нарушение, если мы хотим изменить порядок представлений.





Ваш вид верхнего уровня должен быть TabbedView, и каждая вкладка должна иметь свой собственный NavigationView. Это не касается того факта, что «First» используется как для .navigationBarTitle, так и для .tabItemLabel, но на самом деле это две разные вещи, которые могут быть разными.
struct SomeTabbedView : View {
@State private var selection = 0
var body: some View {
TabbedView(selection: $selection) {
NavigationView {
ContentView()
.navigationBarTitle(Text("First"))
}
.tabItemLabel(Text("First"))
.tag(0)
NavigationView {
OtherView()
.navigationBarTitle(Text("Second"))
}
.tabItemLabel(Text("Second"))
.tag(1)
}.edgesIgnoringSafeArea(.top)
}
}
На самом деле с этим предлагаемым решением содержимое представлений будет перекрываться с NavigationBarTitle, а в моей реализации - нет.
См. документы Apple «Приложение, использующее контроллер панели вкладок, также может использовать контроллеры навигации на одной или нескольких вкладках. При объединении этих двух типов контроллеров представления в одном пользовательском интерфейсе контроллер панели вкладок всегда выступает в качестве оболочки для контроллеров навигации».
Понимаю. Итак, теперь мой TabbedView является родителем NavigationView, из-за чего он не резервирует место для NavigationBarTitle, а скорее инкапсулирует его. Любая идея, как я могу преодолеть это?
Я не знаю, как преодолеть это в настоящее время. Звучит как отличный дополнительный вопрос, если предположить, что на него уже нет ответа.
@zerohedge Я только что понял: вы можете правильно отображать строку заголовка, используя .edgesIgnoringSafeArea(.top) в TabbedView.
У меня была аналогичная проблема, и я решил ее, создав оболочку представления, аналогичную тому, что делается в SwiftUI. По сути, это просто добавление DRYness к ответ вакавамы:
struct NavigationTab<Title, Content>: View where Title: StringProtocol, Content: View {
var title: Title
var content: () -> Content
var body: some View {
NavigationView {
content()
.navigationBarTitle(Text(title))
}
.tabItemLabel(Text(title))
}
}
Я пропустил тег, потому что для меня имеет больше смысла определять те, которые определены в родительском представлении. Вы можете использовать его следующим образом:
struct SomeTabbedView: View {
@State private var selection = 0
var body: some View {
TabbedView(selection: $selection) {
NavigationTab(title: "First") {
ContentView()
}
.tag(0)
NavigationTab(title: "Second") {
OtherView()
}
.tag(1)
}
}
}
Это, очевидно, усложняется, если вы хотите иметь дополнительные детали, такие как изображение над меткой вкладки, поскольку .navigationBarTitle() требует экземпляра Text.
Спасибо. Проголосовал, но некоторое время будет держать это открытым для ответов, которые могут решить СУХОСТЬ этого. Я полностью согласен с тем, что
.tabItemLabelи.navigationBarTitleконцептуально (и часто конкретно) различны, но я все же хотел бы знать, как добиться такого рендеринга представления, например, внутри циклаforEach.