【问题标题】:Remove back button text from navigationbar in SwiftUI从 SwiftUI 中的导航栏中删除后退按钮文本
【发布时间】:2020-06-17 01:21:01
【问题描述】:

我最近开始使用 SwiftUI,得出的结论是使用导航还不是很好。我想要达到的目标如下。我终于设法摆脱了半透明背景而不会导致应用程序崩溃,但现在我遇到了下一个问题。如何摆脱 navbaritem 中的“后退”文本?

我通过像这样在SceneDelegate.swift 文件中设置默认外观来实现上述视图。

let newNavAppearance = UINavigationBarAppearance()
newNavAppearance.configureWithTransparentBackground()
newNavAppearance.setBackIndicatorImage(UIImage(named: "backButton"), transitionMaskImage: UIImage(named: "backButton"))
newNavAppearance.titleTextAttributes = [
    .font: UIFont(name: GTWalsheim.bold.name, size: 18)!,
    .backgroundColor: UIColor.white

]

UINavigationBar.appearance().standardAppearance = newNavAppearance

我可以实现此目的的一种可能方法是覆盖导航栏项目,但是正如此问题的创建者已经说过的那样,这有一个缺点(SwiftUI Custom Back Button Text for NavigationView),在您覆盖导航栏项目后,后退手势停止工作.有了这个,我也想知道如何设置后退按钮的前景颜色。它现在具有默认的蓝色,但是我想用另一种颜色覆盖它。

【问题讨论】:

    标签: ios swift navigation swiftui


    【解决方案1】:

    其实很简单。以下解决方案是我制作的最快和最干净的解决方案。

    例如,将其放在 SceneDelegate 的底部。

    extension UINavigationController {
        // Remove back button text 
        open override func viewWillLayoutSubviews() {
            navigationBar.topItem?.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
        }
    }
    

    这将从您应用中的每个 NavigationView (UINavigationController) 中删除后退按钮文本。

    【讨论】:

    • 谢谢!几天来,我一直在寻找像这样的简单解决方案。
    • 这应该是公认的答案。
    【解决方案2】:

    支持@Pitchbloas 提供的解决方案,此方法只需将backButtonDisplayMode 属性设置为.minimal

    extension UINavigationController {
    
      open override func viewWillLayoutSubviews() {
        navigationBar.topItem?.backButtonDisplayMode = .minimal
      }
    
    }
    

    【讨论】:

    • 我不知道'backButtonDisplayMode',谢谢!
    • 这是一个不错的选择!只是提醒其他人它需要 iOS 14+。
    • 发现这是一个很好的解决方案,但应该在设置按钮显示模式之前调用super.viewWillLayoutSubviews()
    【解决方案3】:

    我找到了一种直接使用 SwiftUI 删除后退按钮文本并保留原始 V 形的简单方法。

    添加了拖动手势以模仿经典的导航返回按钮 当用户想要通过向右滑动返回时。

    import SwiftUI
    
    struct ContentView: View {
    
      @Environment(\.presentationMode) var presentation
    
      var body: some View {
        ZStack {
          // Your main view code here with a ZStack to have the
          // gesture on all the view.
        }
        .navigationBarBackButtonHidden(true)
        .navigationBarItems(
          leading: Button(action: { presentation.wrappedValue.dismiss() }) {
            Image(systemName: "chevron.left")
              .foregroundColor(.blue)
              .imageScale(.large) })
        .contentShape(Rectangle()) // Start of the gesture to dismiss the navigation
        .gesture(
          DragGesture(coordinateSpace: .local)
            .onEnded { value in
              if value.translation.width > .zero
                  && value.translation.height > -30
                  && value.translation.height < 30 {
                presentation.wrappedValue.dismiss()
              }
            }
        )
      }
    }
    

    【讨论】:

    • 这太棒了!我要提一下,presentation.wrappedValue.dismiss 在项目中一直很头疼,有时会在特定情况下导致应用崩溃。
    【解决方案4】:

    所以我实际上最终得到了以下实际可行的解决方案。我正在像这样覆盖导航栏项目

    .navigationBarItems(leading:
        Image("backButton")
            .foregroundColor(.blue)
            .onTapGesture {
                self.presentationMode.wrappedValue.dismiss()
        }
    )
    

    唯一的问题是后退手势不起作用,因此通过实际扩展 UINavigationController 解决了这个问题

    extension UINavigationController: UIGestureRecognizerDelegate {
        override open func viewDidLoad() {
            super.viewDidLoad()
            interactivePopGestureRecognizer?.delegate = self
        }
    
        public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
            return viewControllers.count > 1
        }
    }
    

    现在它看起来正是我想要的方式,解决方案有点老套......但它现在可以工作,希望 SwiftUI 会成熟一点,这样可以更容易地完成。

    【讨论】:

    • 这不会在向后滑动时为您提供重复的导航和返回按钮吗?对我来说就是这样。所以我最终使用工具栏增加了导航标题的宽度。
    【解决方案5】:

    标准返回按钮标题取自前一屏的导航栏标题。

    可以通过以下方法获得所需的效果:

    struct TestBackButtonTitle: View {
        @State private var hasTitle = true
        var body: some View {
            NavigationView {
                NavigationLink("Go", destination:
                    Text("Details")
                        .onAppear {
                            self.hasTitle = false
                        }
                        .onDisappear {
                            self.hasTitle = true
                        }
                )
                .navigationBarTitle(self.hasTitle ? "Master" : "")
            }
        }
    }
    

    【讨论】:

    • 好吧,是的,这是一个可能的解决方案,但它真的很讨厌,如果我必须在每个屏幕上都这样做,那会有点烦人和 hacky。我正在寻找一种全局禁用此文本的方法。
    【解决方案6】:

    自定义 navigationBarItems 和 self.presentationMode.wrappedValue.dismiss() 有效,但不允许执行向后滑动

    您可以添加以下代码以再次滑动

    //perform gesture go back
    extension UINavigationController: UIGestureRecognizerDelegate {
        override open func viewDidLoad() {
            super.viewDidLoad()
            interactivePopGestureRecognizer?.delegate = self
        }
    
        public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
            return viewControllers.count > 1
        }
    }
    

    但问题是,有时它会在你滑动半屏然后取消时让你的应用崩溃。

    我建议使用另一种方法来删除“后退”文本。 添加 isActive 状态来监控当前屏幕是否处于活动状态。 :)

    struct ContentView: View {
        @State var isActive = false
    
        var body: some View {
            NavigationView() {
                NavigationLink(
                    "Next",
                    destination: Text("Second Page").navigationBarTitle("Second"),
                    isActive: $isActive
                )
                    .navigationBarTitle(!isActive ? "Title" : "", displayMode: .inline)
            }
        }
    }
    

    【讨论】:

    • 在 iOS 15.2 上,此(使用 $isActive 绑定的第二个修复)可以禁用后退按钮文本,但会导致在导航中选择随机项目,并且调试控制台显示错误“遇到 SwiftUI推送导航链接时出现问题。请提交错误。"
    【解决方案7】:

    使用这个:

    import UIKit
    
    extension UINavigationController {
        open override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
    
            navigationBar.topItem?.backButtonDisplayMode = .minimal
        }
    }
    

    这比viewWillLayoutSubviews 变体更高效(如果您print viewWillLayoutSubviews 解决方案中的任何内容被打印了很多次,您可以自己检查一下。

    【讨论】:

      【解决方案8】:

      使用Introspect 框架,您可以轻松访问底层导航项并将backButtonDisplayMode 设置为最小。

      这是在被推送的视图中使用它的方法

      var body: some View {
          Text("Your body here")
              .introspectNavigationController { navController in
                  navController.navigationBar.topItem?.backButtonDisplayMode = .minimal
              }
      }
      

      【讨论】: