SwiftUI 底部工具栏在返回时消失。

7

我有这些SwiftUI视图,试图使用toolbar(底部栏)。 当你启动应用程序时,它看起来很好,但是在使用导航链接进入View2后返回主视图后,工具栏会消失。如果NavigationLink在列表中,则会发生这种情况。如果不使用列表(将导航链接放在VStack或类似位置),则工具栏会按预期显示并在返回初始视图时重新出现。是否有方法解决这个问题?enter image description here

import SwiftUI

struct View2: View {
    var body: some View {
        VStack{
            Text("View2")
        }
        
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView{
            List{
                NavigationLink(destination: View2()) {
                    Text("go to View2")
                }
                
            }
            .toolbar(content: {
                ToolbarItem(placement: .bottomBar, content: {
                    Text("toolbar item 1")
                })
            })
        }
        .navigationViewStyle(StackNavigationViewStyle())
            
    }
}

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

这已经被报告了 https://dev59.com/61IG5IYBdhLWcg3wkyGl - Asperi
4个回答

16

更新:已在Xcode 13.3 / iOS 15.4中修复

这是已知的Bug。以下是可能的解决方法 - 强制View2刷新后消失(在Xcode 12.1 / iOS 14.1上进行了测试)

struct ContentView: View {
    @State private var refresh = UUID()

    var body: some View {
        NavigationView{
            List{
                NavigationLink(destination:
                        View2().onDisappear { refresh = UUID() }) { // << here !!
                    Text("go to View2")
                }
            }
            .toolbar(content: {
                 ToolbarItem(placement: .bottomBar, content: {
                      Text("toolbar item 1")
                 })
            }).id(refresh)                     // << here !!

        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

不错的解决方法。它也适用于 xCode 12.2。 - georgeok
2
对我不起作用。XCode 12.4(12D4e)。 - Sunkas
在我的Xcode 12.4中可以运行 - 差点忘记包括onDisappear方法了,它会刷新ID。谢谢! - adamfootdev
1
任何解决方案或建议吗? @asperi的解决方案对我的情况不起作用。 - Vader
但在提供的示例中,即使不使用“刷新”,工具栏也不会消失。 然而,在我的情况下,对于键盘工具栏,它不起作用。 - undefined

2

这种方法更简单,能够帮助一些人。

.toolbar {
      ToolbarItem(id: UUID().uuidString, placement: .bottomBar, showsByDefault: true) {
            AssetToolbarView(selectedCount: 0)
      }        
}

这是有效的解决方案。我一直在使用ToolbarItemGroups,所以切换到了ToolbarItem。我只需要在其中一个项目上执行此操作,即可在返回到父视图时显示工具栏。谢谢! - Gallaugher

0
在我的情况下,我必须确保工具栏仅在View1中可见。为了实现这一点,我使用了一个状态变量来检测用户何时查看View1。
 struct View2: View {
    var body: some View {
        Text("View2")
    }
 }

struct View1: View {
    @State private var refresh = UUID()
    @State var isShown = true
    
    var body: some View {
            NavigationView {
                List {
                    NavigationLink(destination:
                        View2()
                            .onDisappear(perform: {
                                    if (isShown) {
                                        refresh = UUID()
                                    }
                            })
                            .onAppear(perform: { isShown = false })
                    ){
                       Text("Row1")
                    }
                }
                .onAppear(perform: {
                    isShown = true
                })
                .toolbar(content: {
                    ToolbarItem(placement: .bottomBar) {
                    Button(action: {
                        // Action
                    }) {
                        HStack(spacing: 10) {
                        Image(systemName: "plus.circle")
                        Text("Add")
                    }
                    }}
                })
                .id(refresh)
            }
    }
}

0
我找到了一种更适合SwiftUI和iOS的方法来解决在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来取消隐藏。
以下是更新后的代码,在收到"renewToolbar"通知时将状态变量navigationBarIsHidden设置为false。请注意,ContentView中的修饰符.navigationBarHidden(self.navigationBarIsHidden)以及通过Task()延迟将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() {
               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() {
               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(搜索框)而不是Text,以防万一这不是为了Text而调用的。
结果是,当我关闭弹出视图时,工具栏仍然消失,但随后立即重新出现。
我尝试了上面提出的使用视图的id进行更新的解决方案,但对我来说没有起作用。在SwiftUI中,替换id是一项繁重的操作,因为它会强制重新渲染整个视图层次结构。整个视图(在我的情况下:整个屏幕)都会重新构建。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接