SwiftUI .toolbar исчезает после перехода по NavigationLink и возврата

Я добавил.toolbar на верхний уровень NavigationView, который в конечном итоге будет использоваться для выбора элементов в списке без использования жестов смахивания (кнопка вверх, кнопка вниз и т. Д.). У меня также есть.navigationBar для доступа к другим представлениям для учетной записи и настроек.

По большей части это выглядит действительно хорошо, но когда я следую за NavigationLink (в.navigationBarItems) в NavigationView, а затем использую встроенную обратную навигацию, мой.toolbar исчезает с верхнего уровня.

Я кладу.toolbar не в то место? Это похоже на проблему с.navigationViewStyle(StackNavigationViewStyle()), потому что, когда я закомментирую это, панель инструментов не исчезнет при навигации... но мне не нравится, как поведение по умолчанию работает в альбомной ориентации, поэтому я полагаюсь на это.


import SwiftUI

struct ContentView: View {

    var body: some View {

                NavigationView {

                    List {
                        Group {
                            Section(header: Text("List Items").foregroundColor(.gray).font(.footnote)) {
                                Text("List Item One")
                                Text("List Item Two")
                                Text("List Item Three")
                            }
                        }
                   }.navigationTitle("Top Level List").navigationBarTitleDisplayMode(.inline)
                       .ignoresSafeArea(.all)

    // MARK: NAVBAR

                        .navigationBarItems(
                            leading:
                            NavigationLink(destination: UserView()) {
                                Image(systemName: "person.crop.circle").font(.title2)
                            },
                            trailing:
                                NavigationLink(destination: SettingsView()) {
                                    Image(systemName: "gear").font(.title2)
                                })

     //MARK: - CONTENT NAV

                        .toolbar {

                            ToolbarItemGroup(placement: .bottomBar) {

                                Button(action: {}, label: {Label("Mute", systemImage: "speaker.slash.fill")})
                                Spacer()
                                Button(action: {}, label: {Label("Repeat", systemImage: "arrow.clockwise")})
                                Spacer()
                                Button(action: {}, label: {Label("Previous", systemImage: "arrow.up")})
                                Spacer()
                                Button(action: {}, label: {Label("Next", systemImage: "arrow.down")})
                                Spacer()
                                Button(action: {}, label: {Label("Select", systemImage: "arrow.right")})

                            }
                        }
                }.navigationViewStyle(StackNavigationViewStyle())
        }
}

struct UserView: View {

    @State private var username: String = ""
    @State private var password: String = ""

    var body: some View {

                    Form {
                        TextField("Username", text: $username)
                        SecureField("Password", text: $password)
                    }
                    .navigationBarTitle("Account").font(.subheadline)

    }
}

struct SettingsView: View {
    
    @State private var setting1: String = ""
    @State private var setting2: String = ""

    var body: some View {

        Form {
            TextField("Setting One", text: $setting1)
            SecureField("Setting Two", text: $setting2)
        }
        .navigationBarTitle("Settings").font(.subheadline)

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

2 ответа

Вы правы, это не в том месте. Вот как это должно быть, если вам нужно, чтобы панель инструментов всегда отображалась:

      struct ContentView: View {
    
    var body: some View {
        
        NavigationView {
            
            List {
                Group {
                    Section(header: Text("List Items").foregroundColor(.gray).font(.footnote)) {
                        Text("List Item One")
                        Text("List Item Two")
                        Text("List Item Three")
                    }
                }
            }.navigationTitle("Top Level List").navigationBarTitleDisplayMode(.inline)
                .ignoresSafeArea(.all)
            
            // MARK: NAVBAR
            
                .navigationBarItems(
                    leading:
                        NavigationLink(destination: UserView()) {
                            Image(systemName: "person.crop.circle").font(.title2)
                        },
                    trailing:
                        NavigationLink(destination: SettingsView()) {
                            Image(systemName: "gear").font(.title2)
                        })
            
            //MARK: - CONTENT NAV
            
        }.navigationViewStyle(StackNavigationViewStyle())
            .toolbar {
                
                ToolbarItemGroup(placement: .bottomBar) {
                    
                    Button(action: {}, label: {Label("Mute", systemImage: "speaker.slash.fill")})
                    Spacer()
                    Button(action: {}, label: {Label("Repeat", systemImage: "arrow.clockwise")})
                    Spacer()
                    Button(action: {}, label: {Label("Previous", systemImage: "arrow.up")})
                    Spacer()
                    Button(action: {}, label: {Label("Next", systemImage: "arrow.down")})
                    Spacer()
                    Button(action: {}, label: {Label("Select", systemImage: "arrow.right")})
                    
                }
            }
    }
}

Я нашел более адаптированный к SwiftUI подход для решения проблемы исчезновения панели инструментов (панели навигации) в SwiftUI и iOS. Я использую панель инструментов для очень важных команд — замены меню приложений в macOS. Итак, отсутствие панели инструментов наносит вред моему iOS-приложению. Это никогда не должно (!) произойти. Однако в различных ситуациях оно исчезало (например, когда я закрывал всплывающее окно или прокручивал главное окно). Для этого я, кажется, нашел твердое решение. Сначала код шаблона для создания панели инструментов — это символический код для создания базового представления контента в моем приложении.

      struct ContentView: View {
   
   var body: some View {
      
      // The NavigationStack ensures the presence of the navigationBar (toolBar)
      let theView =
      NavigationStack(root: {self.generateContentView()})
      
      return theView
   }
   
   func generateContentView() -> some View {
      let theView =
         Text("This is my content view")
       .toolbar {self.generateToolBar()}
   }

   @ToolbarContentBuilder
   func generateToolBar() -> some ToolbarContent {
      ToolbarItem(placement: .navigationBarTrailing) {
         Text("This is a toolbar item")
      }
   }
}

Мое решение заключалось в том, что если я не могу помешать системе принимать решения не показывать панель инструментов, я могу попытаться отменить скрытие, установив для переменной состояния NavigationBarIsHidden значение false.

Здесь обновленный код устанавливает для переменной состояния naviationBarIsHidden значение false при получении уведомления «renewToolbar». Обратите внимание на модификатордля ContentView и отложенное изменение NavigationBarIsHidden на false с помощью.

      struct ContentView: View {
   
   let document:ApplicationDocument
   
   @State var navigationBarIsHidden : Bool = false
   
   var body: some View {
      
      // The NavigationStack ensures the presence of the navigationBar (toolBar)
      let theView =
      NavigationStack(root: {self.generateContentView()})
         .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(rawValue: "renewToolbar"), object: self.document), perform: { notification in
            self.navigationBarIsHidden = true
            Task() {@MainActor () -> Void in 
            try? await Task.sleep(for:.milliseconds(1000))
               self.navigationBarIsHidden = false 
            }})
      
      return theView
   }
   
   func generateContentView() -> some View {
      let theView =
         Text("This is my content view")
       .toolbar {self.generateToolBar()}
       .navigationBarHidden(self.navigationBarIsHidden)
   }

   @ToolbarContentBuilder
   func generateToolBar() -> some ToolbarContent {
      ToolbarItem(placement: .navigationBarTrailing) {
         Text("This is a toolbar item")
      }
   }
}

Остался вопрос: когда мне отправлять это уведомление, если такое разнообразие событий может привести к исчезновению панели навигации? Решением было привязать это к модификатору .onDisappear одного из элементов панели инструментов.

Итак, вот полный код:

      struct ContentView: View {
   
   let document:ApplicationDocument
   
   @State var navigationBarIsHidden : Bool = false
   
   var body: some View {
      
      // The NavigationStack ensures the presence of the navigationBar (toolBar)
      let theView =
      NavigationStack(root: {self.generateContentView()})
         .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(rawValue: "renewToolbar"), object: self.document), perform: { notification in
            self.navigationBarIsHidden.toggle()
            Task() {@MainActor () -> Void in
            try? await Task.sleep(for:.milliseconds(1000)) 
               self.navigationBarIsHidden.toggle()
            }})
      
      return theView
   }
   
   func generateContentView() -> some View {
      let theView =
         Text("This is my content view")
       .toolbar {self.generateToolBar()}
       .navigationBarHidden(self.navigationBarIsHidden)
   }

   @ToolbarContentBuilder
   func generateToolBar() -> some ToolbarContent {
      ToolbarItem(placement: .navigationBarTrailing) {
         Text("This is a toolbar item")
            .onDisappear(perform: {
               NotificationCenter.default.post(name: NSNotification.Name(rawValue: "renewToolbar"), object: self.document)
            })
      }
   }
}

В моем реальном приложении уведомление фактически привязано к TextField (полю поиска), а не к тексту, на тот случай, если это не вызывается для текста.

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

Я попробовал решение с обновлением идентификатора представления, как предложено выше. У меня это не сработало. Замена идентификатора — это сложное действие в SwiftUI, поскольку при этом требуется повторная отрисовка всей иерархии представлений. Весь вид (в моем случае: весь экран) перестраивается.

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