SwiftUI: лучший способ изменить метку TabbedView на основе NavigationBarTitle

Мне наконец удалось выяснить, как изменить заголовок моего 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, но я не только не уверен, что это правильный путь, я не уверен, как продолжить его реализацию. Также можно просто иметь массив с метками, но это создает еще одно нарушение, если мы хотим изменить порядок представлений.

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

Ответы 2

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

Ваш вид верхнего уровня должен быть 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)
    }
}

Спасибо. Проголосовал, но некоторое время будет держать это открытым для ответов, которые могут решить СУХОСТЬ этого. Я полностью согласен с тем, что .tabItemLabel и .navigationBarTitle концептуально (и часто конкретно) различны, но я все же хотел бы знать, как добиться такого рендеринга представления, например, внутри цикла forEach.

zerohedge 12.06.2019 17:20

На самом деле с этим предлагаемым решением содержимое представлений будет перекрываться с NavigationBarTitle, а в моей реализации - нет.

zerohedge 12.06.2019 18:18

См. документы Apple «Приложение, использующее контроллер панели вкладок, также может использовать контроллеры навигации на одной или нескольких вкладках. При объединении этих двух типов контроллеров представления в одном пользовательском интерфейсе контроллер панели вкладок всегда выступает в качестве оболочки для контроллеров навигации».

vacawama 12.06.2019 18:24

Понимаю. Итак, теперь мой TabbedView является родителем NavigationView, из-за чего он не резервирует место для NavigationBarTitle, а скорее инкапсулирует его. Любая идея, как я могу преодолеть это?

zerohedge 12.06.2019 18:31

Я не знаю, как преодолеть это в настоящее время. Звучит как отличный дополнительный вопрос, если предположить, что на него уже нет ответа.

vacawama 12.06.2019 18:34

@zerohedge Я только что понял: вы можете правильно отображать строку заголовка, используя .edgesIgnoringSafeArea(.top) в TabbedView.

ByteDuck 08.07.2019 18:26

У меня была аналогичная проблема, и я решил ее, создав оболочку представления, аналогичную тому, что делается в 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.

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