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». Обратите внимание на модификатор
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, поскольку при этом требуется повторная отрисовка всей иерархии представлений. Весь вид (в моем случае: весь экран) перестраивается.