简体   繁体   English

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

[英]Remove back button text from navigationbar in SwiftUI

I've recently started working in SwiftUI, came to the conclusion that working with navigation isn't really great yet.我最近开始在 SwiftUI 工作,得出的结论是使用导航还不是很好。 What I'm trying to achieve is the following.我想要实现的是以下内容。 I finally managed to get rid of the translucent background without making the application crash, but now I ran into the next issue.我终于设法在不使应用程序崩溃的情况下摆脱了半透明背景,但现在我遇到了下一个问题。 How can I get rid of the "back" text inside the navbaritem?我怎样才能摆脱 navbaritem 中的“后退”文本?

在此处输入图像描述

I achieved the view above by setting the default appearance in the SceneDelegate.swift file like this.我通过像这样在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

One possible way that I could achieve this is by overriding the navigation bar items, however this has one downside ( SwiftUI Custom Back Button Text for NavigationView ) as the creator of this issue already said, the back gesture stops working after you override the navigation bar items.我可以实现这一点的一种可能方法是覆盖导航栏项目,但是这有一个缺点( SwiftUI NavigationView 的自定义后退按钮文本)正如本期的创建者已经说过的那样,在您覆盖导航栏后后退手势停止工作项目。 With that I'm also wondering how I could set the foregroundColor of the back button.有了这个,我也想知道如何设置后退按钮的 foregroundColor 。 It now has the default blue color, however I'd like to overwrite this with another color.它现在具有默认的蓝色,但是我想用另一种颜色覆盖它。

It's actually really easy.这真的很容易。 The following solution is the fastest and cleanest i made.以下解决方案是我制作的最快和最干净的解决方案。

Put this at the bottom of your SceneDelegate for example.例如,将其放在 SceneDelegate 的底部。

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

This will remove the back button text from every NavigationView (UINavigationController) in your app.这将从应用程序中的每个 NavigationView (UINavigationController) 中删除后退按钮文本。

Piggy-backing on the solution @Pitchbloas offered, this method just involves setting the backButtonDisplayMode property to .minimal :借助@Pitchbloas 提供的解决方案,此方法仅涉及将backButtonDisplayMode属性设置为.minimal

extension UINavigationController {

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

}

I have found a straightforward approach to remove the back button text using SwiftUI only, and keeping the original chevron.我找到了一种仅使用 SwiftUI 删除后退按钮文本并保留原始 V 形的简单方法。

A drag gesture is added to mimic the classic navigation back button when user wants to go back by swiping right.当用户想要通过向右滑动返回时,添加了一个拖动手势以模仿经典的导航返回按钮。

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()
          }
        }
    )
  }
}

So I actually ended up with the following solution that actually works.所以我实际上最终得到了以下实际可行的解决方案。 I am overwriting the navigation bar items like so我像这样覆盖导航栏项目

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

The only issue with this was that the back gesture wasn't working so that was solved by actually extending the UINavigationController唯一的问题是后退手势不起作用,因此通过实际扩展UINavigationController解决了这个问题

extension UINavigationController: UIGestureRecognizerDelegate {
    override open func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }
}

Now it's looking exactly the way I want it, the solution is kinda hacky... but it works for now, hopefully SwiftUI will mature a little bit so this can be done easier.现在它看起来正是我想要的方式,解决方案有点老套......但它现在可以工作,希望 SwiftUI 会成熟一点,这样可以更容易地完成。

Standard Back button title is taken from navigation bar title of previous screen.标准后退按钮标题取自前一屏幕的导航栏标题。

It is possible the following approach to get needed effect:可以通过以下方法获得所需的效果:

演示

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

Using the Introspect framework, you can easily gain access to the underlying navigation item and set the backButtonDisplayMode to minimal.使用Introspect框架,您可以轻松访问底层导航项并将backButtonDisplayMode设置为最小。

Here's how you might use that in the view that was pushed这是在推送的视图中使用它的方法

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

custom navigationBarItems and self.presentationMode.wrappedValue.dismiss() worked but you are not allow to perform swiping back自定义 navigationBarItems 和 self.presentationMode.wrappedValue.dismiss() 有效,但不允许执行向后滑动

You can either add the following code to make the swipe back again您可以添加以下代码以再次滑动

//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
    }
}

but the problem is, sometimes it will make your app crashed when you swipe half the screen and then cancel.但问题是,有时它会让你的应用程序在你滑动一半屏幕然后取消时崩溃。

I would suggest the other way to remove the "Back" text.我建议以另一种方式删除“返回”文本。 Adding the isActive state to monitor whether the current screen is active or not.添加 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)
        }
    }
}

I am accomplishing this by changing the title of the master screen before pushing the detail screen and then setting it back when it re-appears.我通过在推送详细屏幕之前更改主屏幕的标题并在它重新出现时将其设置回来来实现这一点。 The only caveat is when you go back to the master screen the title's re-appearance is a little noticeable.唯一需要注意的是,当您返回主屏幕时,标题的重新出现有点明显。

Summary:概括:

  • on master view add state var (eg isDetailShowing) to store if detail screen is showing or not在主视图上添加状态变量(例如 isDetailShowing)以存储是否显示详细信息屏幕

  • on master view use the navigationTitle modifier to set the title based on the current value of isDetailShowing在主视图上使用 navigationTitle 修饰符根据 isDetailShowing 的当前值设置标题

  • on master view use onAppear modifier to set the value of isDetailShowing to false在主视图上使用 onAppear 修饰符将 isDetailShowing 的值设置为 false

  • on the NavigationLink in master screen use the simultaneousGesture modifier to set the isDetailShowing to true在主屏幕的 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") } }

If you want to:如果你想:

  • Do it globally在全球范围内进行
  • Keep the standard back button (along with custom behaviours like an ability to navigate a few screens back on a long press)保留标准的后退按钮(以及自定义行为,例如长按导航几个屏幕的能力)
  • Avoid introducing any third party frameworks避免引入任何第三方框架

You can do it by setting the back button text color to Clear Color via appearance:您可以通过外观将后退按钮文本颜色设置为清除颜色:

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

You can do it once when the app starts and forget about it.您可以在应用程序启动时执行一次并忘记它。

A potential downside (depending on your preferences) is that the transition to the clear color is animated as the title of the current window slides to the left as you move to a different one.一个潜在的缺点(取决于您的偏好)是,当您移动到另一个标题时,当前 window 的标题会向左滑动,从而过渡到清晰的颜色。

You can also experiment with different text attributes.您还可以尝试不同的文本属性。

演示

Works on iOS 16适用于 iOS 16

Solutions above didn't work for me.上面的解决方案对我不起作用。 I wanted to make changes specific to view without any global (appearance or extension) and with minimal boilerplate code.我想对视图进行特定的更改,而不需要任何全局(外观或扩展)并且使用最少的样板代码。

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

  1. Get visible View Controller.获取可见视图 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. Update NavigationItem inside init of the View.在视图的 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
        }
    }

}

Why not use Custom BackButton with Default Back Button Hidden为什么不使用隐藏默认后退按钮的自定义后退按钮

You could use Any Design You Prefer !你可以使用任何你喜欢的设计!

Works on iOS 16适用于 iOS 16

First View第一个视图

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

Second View第二视图

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

you can use.toolbarRole(.editor)你可以使用.toolbarRole(.editor)

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

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