[英]Custom back button for NavigationView's navigation bar in SwiftUI
我想添加一个看起来像这样的自定义导航按钮:
现在,我为此编写了一个自定义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()) {}
在 DetailsView 中隐藏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
和.navigationBarItems
来获得您想要达到的效果。
struct Navigation_CustomBackButton_Detail: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
ZStack {
Color("Theme3BackgroundColor")
VStack(spacing: 25) {
Image(systemName: "globe").font(.largeTitle)
Text("NavigationView").font(.largeTitle)
Text("Custom Back Button").foregroundColor(.gray)
HStack {
Image("NavBarBackButtonHidden")
Image(systemName: "plus")
Image("NavBarItems")
}
Text("Hide the system back button and then use the navigation bar items modifier to add your own.")
.frame(maxWidth: .infinity)
.padding()
.background(Color("Theme3ForegroundColor"))
.foregroundColor(Color("Theme3BackgroundColor"))
Spacer()
}
.font(.title)
.padding(.top, 50)
}
.navigationBarTitle(Text("Detail View"), displayMode: .inline)
.edgesIgnoringSafeArea(.bottom)
// Hide the system back button
.navigationBarBackButtonHidden(true)
// Add your custom back button here
.navigationBarItems(leading:
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image(systemName: "arrow.left.circle")
Text("Go Back")
}
})
}
}
基于此处的其他答案,这是选项 2 在 XCode 11.0 中为我工作的简化答案:
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
}
}
这是一个更精简的版本,它使用其他评论中显示的原则仅更改按钮的文本。 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")
}
您可以为此使用UIAppearance
:
if let image = UIImage(named: "back-button") {
UINavigationBar.appearance().backIndicatorImage = image
UINavigationBar.appearance().backIndicatorTransitionMaskImage = image
}
这应该在您的应用程序中尽早添加,例如App.init
。 这也保留了本机向后滑动功能。
我在这里看到的所有解决方案似乎都禁用了滑动返回功能以导航到上一页,因此分享我发现的维护该功能的解决方案。 您可以扩展您的根视图并覆盖您的导航样式并在视图初始化程序中调用该函数。
示例视图
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()
}
}
真的很简单的方法。 只有两行代码🔥
@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")
}
}
}
我发现了这个: 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")
}
}
}
presentationMode.wrappedValue.dismiss()
现在已弃用。
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.