SwiftUI中为NavigationView的导航栏自定义返回按钮

81

我想添加一个自定义导航按钮,大概看起来像这样:

期望的导航返回按钮

现在,我已经为此编写了一个自定义的BackButton视图。将该视图应用为前导导航条项时,执行以下操作:

.navigationBarItems(leading: BackButton())

导航视图看起来像这样:

当前导航返回按钮

我已经尝试过像这样的修改器:

.navigationBarItem(title: Text(""), titleDisplayMode: .automatic, hidesBackButton: true)

没有任何运气。

问题

我如何...

  1. 设置用作导航栏自定义返回按钮的视图?或者:
  2. 以编程方式将视图弹回其父级?
    进行此方法时,我可以使用.navigationBarHidden(true)完全隐藏导航栏。

改进版。(Swift,iOS 13 beta 4)https://dev59.com/e7Tna4cB1Zd3GeqPBNoQ#57098885 - frogcjn
14个回答

1
这个解决方案适用于 iPhone。但是,对于 iPad,由于 splitView 的问题,它将无法使用。
import SwiftUI

struct NavigationBackButton: View {
  var title: Text?
  @Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>

  var body: some View {
    ZStack {
      VStack {
        ZStack {
          HStack {
            Button(action: {
              self.presentationMode.wrappedValue.dismiss()
            }) {
              Image(systemName: "chevron.left")
                .font(.title)
                .frame(width: 44, height: 44)
              title
            }
            Spacer()
          }
        }
        Spacer()
      }
    }
    .zIndex(1)
    .navigationBarTitle("")
    .navigationBarHidden(true)
  }
}

struct NavigationBackButton_Previews: PreviewProvider {
  static var previews: some View {
    NavigationBackButton()
  }
}

0
在iOS 14+上,使用presentationMode变量实际上非常容易。
在这个例子中,NewItemView将在完成addItem后被解除显示:
struct NewItemView: View {
    @State private var itemDescription:String = ""
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    
    
    var body: some View {
        VStack {
            TextEditor(text: $itemDescription)
        }.onTapGesture {
            hideKeyboard()
        }.toolbar {
            
            ToolbarItem {
                Button(action: addItem){
                    Text("Save")
                }
            }
            
        }.navigationTitle("Add Question")
        
    }
    private func addItem() {
        // Add save logic
        // ...
        
        // Dismiss on complete
        presentationMode.wrappedValue.dismiss()
    }
    
    private func hideKeyboard() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

struct NewItemView_Previews: PreviewProvider {
    static var previews: some View {
        NewItemView()
    }
}

如果您需要父(主)视图:

struct SampleMainView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \DbQuestion.timestamp, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    NavigationLink {
                        Text("This is item detail page")
                    } label: {
                        Text("Item at \(item.id)")
                    }
                }
                
            }
            .toolbar {
                ToolbarItem {
                        // Creates a button on toolbar
                        NavigationLink {
                            // New Item Page
                            NewItemView()
                        } label: {
                            Text("Add item")
                        }
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
            }.navigationTitle("Main Screen")
            
        }
    }
}

0

只需写下这个:

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {

        }.onAppear() {
            UINavigationBar.appearance().tintColor = .clear
            UINavigationBar.appearance().backIndicatorImage = UIImage(named: "back")?.withRenderingMode(.alwaysOriginal)
            UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "back")?.withRenderingMode(.alwaysOriginal)
        }
    }
}

1
它有点“能用”,但新的返回按钮图像没有对齐正确。代码可能需要一些调整。另外,也许把它放在init(){}里面会更好? - MVZ

0
我发现了这个:https://ryanashcraft.me/swiftui-programmatic-navigation/ 它确实有效,而且可能为控制显示内容的状态机打下基础,但它并不像以前那样简单。
import Combine
import SwiftUI

struct DetailView: View {
    var onDismiss: () -> Void

    var body: some View {
        Button(
            "Here are details. Tap to go back.",
            action: self.onDismiss
        )
    }
}

struct RootView: View {
    var link: NavigationDestinationLink<DetailView>
    var publisher: AnyPublisher<Void, Never>

    init() {
        let publisher = PassthroughSubject<Void, Never>()
        self.link = NavigationDestinationLink(
            DetailView(onDismiss: { publisher.send() }),
            isDetail: false
        )
        self.publisher = publisher.eraseToAnyPublisher()
    }

    var body: some View {
        VStack {
            Button("I am root. Tap for more details.", action: {
                self.link.presented?.value = true
            })
        }
            .onReceive(publisher, perform: { _ in
                self.link.presented?.value = false
            })
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            RootView()
        }
    }
}

If you want to hide the button then you can replace the DetailView with this:

struct LocalDetailView: View {
    var onDismiss: () -> Void

    var body: some View {
        Button(
            "Here are details. Tap to go back.",
            action: self.onDismiss
        )
            .navigationBarItems(leading: Text(""))
    }
}

你知道你可以编辑你的回答吗?不要发表新的回答,而是点击你的回答底部的小 编辑 按钮。然后,删除这个回答。 - LinusGeffarth
改进版。(Swift,iOS 13 beta 4)https://dev59.com/e7Tna4cB1Zd3GeqPBNoQ#57098885 - frogcjn
有趣的是,我因为发布链接而被猛烈抨击,甚至导致我的账户受到严重限制,但是这个最新的回答却完全通过了。 - John Endres

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