繁体   English   中英

从 SwiftUI 中的导航栏中删除后退按钮文本

[英]Remove back button text from navigationbar in SwiftUI

我最近开始在 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 NavigationView 的自定义后退按钮文本)正如本期的创建者已经说过的那样,在您覆盖导航栏后后退手势停止工作项目。 有了这个,我也想知道如何设置后退按钮的 foregroundColor 。 它现在具有默认的蓝色,但是我想用另一种颜色覆盖它。

这真的很容易。 以下解决方案是我制作的最快和最干净的解决方案。

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

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

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

借助@Pitchbloas 提供的解决方案,此方法仅涉及将backButtonDisplayMode属性设置为.minimal

extension UINavigationController {

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

}

我找到了一种仅使用 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()
          }
        }
    )
  }
}

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

.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 会成熟一点,这样可以更容易地完成。

标准后退按钮标题取自前一屏幕的导航栏标题。

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

演示

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" : "")
        }
    }
}

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

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

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

自定义 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)
        }
    }
}

我通过在推送详细屏幕之前更改主屏幕的标题并在它重新出现时将其设置回来来实现这一点。 唯一需要注意的是,当您返回主屏幕时,标题的重新出现有点明显。

概括:

  • 在主视图上添加状态变量(例如 isDetailShowing)以存储是否显示详细信息屏幕

  • 在主视图上使用 navigationTitle 修饰符根据 isDetailShowing 的当前值设置标题

  • 在主视图上使用 onAppear 修饰符将 isDetailShowing 的值设置为 false

  • 在主屏幕的 NavigationLink 上,使用同步手势修饰符将 isDetailShowing 设置为 true

     struct MasterView: View { @State var isDetailShowing = false var body: some View { VStack { Spacer() .frame(height: 20) Text("Master Screen") .frame(maxWidth: .infinity, alignment: .leading) Spacer() .frame(height: 20) NavigationLink(destination: DetailView()) { Text("Go to detail screen") } .simultaneousGesture(TapGesture().onEnded() { isDetailShowing = true }) } .navigationBarTitleDisplayMode(.inline) .navigationTitle(isDetailShowing ? "" : "Master Screen Title") .onAppear() { isDetailShowing = false } } } struct DetailView: View { var body: some View { Text("This is the detail screen") .navigationBarTitleDisplayMode(.inline) .navigationTitle("Detail Screen Title") } }

如果你想:

  • 在全球范围内进行
  • 保留标准的后退按钮(以及自定义行为,例如长按导航几个屏幕的能力)
  • 避免引入任何第三方框架

您可以通过外观将后退按钮文本颜色设置为清除颜色:

let navigationBarAppearance = UINavigationBarAppearance()

let backButtonAppearance = UIBarButtonItemAppearance(style: .plain)
backButtonAppearance.focused.titleTextAttributes = [.foregroundColor: UIColor.clear]
backButtonAppearance.disabled.titleTextAttributes = [.foregroundColor: UIColor.clear]
backButtonAppearance.highlighted.titleTextAttributes = [.foregroundColor: UIColor.clear]
backButtonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.clear]

navigationBarAppearance.backButtonAppearance = backButtonAppearance

//Not sure you'll need both of these, but feel free to adjust to your needs.
UINavigationBar.appearance().standardAppearance = navigationBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance

您可以在应用程序启动时执行一次并忘记它。

一个潜在的缺点(取决于您的偏好)是,当您移动到另一个标题时,当前 window 的标题会向左滑动,从而过渡到清晰的颜色。

您还可以尝试不同的文本属性。

演示

适用于 iOS 16

上面的解决方案对我不起作用。 我想对视图进行特定的更改,而不需要任何全局(外观或扩展)并且使用最少的样板代码。

Since you can update NavigationItem inside the init of the View. You can solve this in 2 steps:

  1. 获取可见视图 Controller。
// Get Visible ViewController
extension UIApplication {
    
    static var visibleVC: UIViewController? {
        var currentVC = UIApplication.shared.windows.first { $0.isKeyWindow }?.rootViewController
        while let presentedVC = currentVC?.presentedViewController {
            if let navVC = (presentedVC as? UINavigationController)?.viewControllers.last {
                currentVC = navVC
            } else if let tabVC = (presentedVC as? UITabBarController)?.selectedViewController {
                currentVC = tabVC
            } else {
                currentVC = presentedVC
            }
        }
        return currentVC
    }
    
}
  1. 在视图的 init 中更新 NavigationItem。
struct YourView: View {
    
    init(hideBackLabel: Bool = true) {
        if hideBackLabel {
            // iOS 14+
            UIApplication.visibleVC?.navigationItem.backButtonDisplayMode = .minimal
            
            // iOS 13-
            let button = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
            UIApplication.visibleVC?.navigationItem.backBarButtonItem = button
        }
    }

}

为什么不使用隐藏默认后退按钮的自定义后退按钮

你可以使用任何你喜欢的设计!

适用于 iOS 16

第一个视图

struct ContentView: View {
var body: some View {
    NavigationView {
        VStack(){
            Spacer()
            NavigationLink(destination: View2()) {
                Text("Navigate")
                    .font(.title)
            }
            Spacer()
        }
    }
}
}

第二视图

struct View2: View {
@Environment(\.presentationMode) var presentationMode

var body: some View {
    NavigationView {
        ZStack{
            VStack{
                HStack(alignment:.center){
//Any Design You Like
                    Image(systemName: "chevron.left")
                           .font(.title)
                           .foregroundColor(.blue)
                           .onTapGesture {                          
self.presentationMode.wrappedValue.dismiss()
                           }
                           .padding()
                   Spacer()
                }
                Spacer()
            }
        }
    }
    .navigationBarBackButtonHidden(true)
}
}

你可以使用.toolbarRole(.editor)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM