我想添加一个自定义导航按钮,看起来有点像这样:
现在,我为此编写了一个自定义
BackButton
视图。当将该视图应用为主要导航栏项目时,请执行以下操作:
.navigationBarItems(leading: BackButton())
...导航视图如下所示:
我尝试过如下修饰符:
.navigationBarItem(title: Text(""), titleDisplayMode: .automatic, hidesBackButton: true)
没有任何运气。
我怎样才能...
.navigationBarHidden(true)
使用它来转换到您的视图:
NavigationLink(destination: SampleDetails()) {}
将其添加到视图本身:
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
然后,在按钮操作或其他操作中,关闭视图:
presentationMode.wrappedValue.dismiss()
从家长那里,使用
NavigationLink
进行导航
NavigationLink(destination: SampleDetails()) {}
在详细信息视图中隐藏
navigationBarBackButton
并将自定义后退按钮设置为前导 navigationBarItem
,
struct SampleDetails: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var btnBack : some View { Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image("ic_back") // set image here
.aspectRatio(contentMode: .fit)
.foregroundColor(.white)
Text("Go back")
}
}
}
var body: some View {
List {
Text("sample code")
}
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: btnBack)
}
}
看起来您现在可以组合
navigationBarBackButtonHidden
和 .toolbar
来获得您想要实现的效果。
struct Navigation_CustomBackButton: View {
var body: some View {
NavigationStack {
NavigationLink("Go To Detail",
destination: Navigation_CustomBackButton_Detail())
.font(.title)
.navigationTitle("Navigation Views")
}
}
}
// Second Screen
struct Navigation_CustomBackButton_Detail: View {
@Environment(\.dismiss) var dismiss
var body: some View {
VStack {
}
.navigationTitle("Detail View")
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button(action: {
dismiss()
}) {
Label("Back", systemImage: "arrow.left.circle")
}
}
}
}
}
presentationMode.wrappedValue.dismiss()
现已弃用。
DismissAction
private struct SheetContents: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
Button("Done") {
dismiss()
}
}
}
您可以创建一个自定义后退按钮来使用此关闭操作
struct NavBackButton: View {
let dismiss: DismissAction
var body: some View {
Button {
dismiss()
} label: {
Image("...custom back button here")
}
}
}
然后将其附加到您的视图中。
.navigationBarBackButtonHidden(true) // Hide default button
.navigationBarItems(leading: NavBackButton(dismiss: self.dismiss)) // Attach custom button
基于此处的其他答案,这是在 XCode 11.0 中为我工作的选项 2 的简化答案:
struct DetailView: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "gobackward").padding()
}
.navigationBarHidden(true)
}
}
注意:为了隐藏NavigationBar,我还需要在ContentView中设置然后隐藏NavigationBar。
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView()) {
Text("Link").padding()
}
} // Main VStack
.navigationBarTitle("Home")
.navigationBarHidden(true)
} //NavigationView
}
}
您可以使用
UIAppearance
为此:
if let image = UIImage(named: "back-button") {
UINavigationBar.appearance().backIndicatorImage = image
UINavigationBar.appearance().backIndicatorTransitionMaskImage = image
}
这应该尽早添加到您的应用程序中,例如
App.init
。这也保留了本机的向后滑动功能。
这是一个更简洁的版本,使用其他评论中显示的原则仅更改按钮的文本。
chevron.left
图标也可以轻松替换为其他图标。
创建您自己的按钮,然后使用 .navigationBarItems() 分配它。我发现以下格式最接近默认的后退按钮。
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var backButton : some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack(spacing: 0) {
Image(systemName: "chevron.left")
.font(.title2)
Text("Cancel")
}
}
}
确保使用
.navigationBarBackButtonHidden(true)
隐藏默认按钮并将其替换为您自己的按钮!
List(series, id:\.self, selection: $selection) { series in
Text(series.SeriesLabel)
}
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: backButton)
我希望您想在所有可导航屏幕中使用自定义后退按钮, 所以我根据@Ashish 答案编写了自定义包装器。
struct NavigationItemContainer<Content>: View where Content: View {
private let content: () -> Content
@Environment(\.presentationMode) var presentationMode
private var btnBack : some View { Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image("back_icon") // set image here
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
Text("Go back")
}
}
}
var body: some View {
content()
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: btnBack)
}
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
}
将屏幕内容包裹在 NavigationItemContainer 中:
用途:
struct CreateAccountScreenView: View {
var body: some View {
NavigationItemContainer {
VStack(spacing: 21) {
AppLogoView()
//...
}
}
}
}
这样滑动不会被禁用。
对我有用。 XCode 11.3.1
将其放入根视图中
init() {
UINavigationBar.appearance().isUserInteractionEnabled = false
UINavigationBar.appearance().backgroundColor = .clear
UINavigationBar.appearance().barTintColor = .clear
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().tintColor = .clear
}
这在您的孩子视图中
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
Button(action: {self.presentationMode.wrappedValue.dismiss()}) {
Image(systemName: "gobackward")
}
非常简单的方法。只有两行代码🔥
@Environment(\.presentationMode) var presentationMode
self.presentationMode.wrappedValue.dismiss()
示例:
import SwiftUI
struct FirstView: View {
@State var showSecondView = false
var body: some View {
NavigationLink(destination: SecondView(),isActive : self.$showSecondView){
Text("Push to Second View")
}
}
}
struct SecondView : View{
@Environment(\.presentationMode) var presentationMode
var body : some View {
Button(action:{ self.presentationMode.wrappedValue.dismiss() }){
Text("Go Back")
}
}
}
我在这里看到的所有解决方案似乎都禁用了滑动返回功能以导航到上一页,因此分享我发现的保留该功能的解决方案。您可以扩展根视图并覆盖您的导航样式并调用视图初始值设定项中的函数。
示例视图
struct SampleRootView: View {
init() {
overrideNavigationAppearance()
}
var body: some View {
Text("Hello, World!")
}
}
扩展
extension SampleRootView {
func overrideNavigationAppearance() {
let navigationBarAppearance = UINavigationBarAppearance()
let barAppearace = UINavigationBar.appearance()
barAppearace.tintColor = *desired UIColor for icon*
barAppearace.barTintColor = *desired UIColor for icon*
navigationBarAppearance.setBackIndicatorImage(*desired UIImage for custom icon*, transitionMaskImage: *desired UIImage for custom icon*)
UINavigationBar.appearance().standardAppearance = navigationBarAppearance
UINavigationBar.appearance().compactAppearance = navigationBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance
}
}
这种方法的唯一缺点是我还没有找到删除/更改与自定义后退按钮关联的文本的方法。
此解决方案适用于 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()
}
}
我发现了这个: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(""))
}
}
就这样写:
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)
}
}
}
在 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")
}
}
}